directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From akaras...@apache.org
Subject svn commit: rev 21633 - in incubator/directory/eve/branches/start: . docs docs/design docs/issues src src/antlr src/conf src/docbook src/docbook/design src/docbook/design/operations src/images src/java src/java/org src/java/org/apache src/java/org/apache/eve src/java/org/apache/eve/backend src/java/org/apache/eve/backend/jdbm src/java/org/apache/eve/backend/jdbm/gui src/java/org/apache/eve/backend/jdbm/index src/java/org/apache/eve/backend/jdbm/search src/java/org/apache/eve/backend/jdbm/table src/java/org/apache/eve/backend/sql src/java/org/apache/eve/client src/java/org/apache/eve/decoder src/java/org/apache/eve/encoder src/java/org/apache/eve/event src/java/org/apache/eve/event/protocol src/java/org/apache/eve/input src/java/org/apache/eve/jndi src/java/org/apache/eve/listener src/java/org/apache/eve/output src/java/org/apache/eve/protocol src/java/org/apache/eve/protocol/event src/java/org/apache/eve/protocol/extended src/java/org/apache/eve/schema src/java/org/apache/eve/security src/java/org/apache/eve/security/auth src/java/org/apache/eve/seda src/ldif src/schema src/xdocs src/xdocs/images src/xdocs/images/design xdocs xdocs/design xdocs/design/operations xdocs/stylesheets
Date Thu, 24 Jun 2004 07:06:37 GMT
Author: akarasulu
Date: Thu Jun 24 00:06:35 2004
New Revision: 21633

Added:
   incubator/directory/eve/branches/start/
   incubator/directory/eve/branches/start/.classpath
   incubator/directory/eve/branches/start/.cvsignore
   incubator/directory/eve/branches/start/.project
   incubator/directory/eve/branches/start/ChangeLog.txt   (contents, props changed)
   incubator/directory/eve/branches/start/LICENSE.txt   (contents, props changed)
   incubator/directory/eve/branches/start/SearchProblems.txt   (contents, props changed)
   incubator/directory/eve/branches/start/build.xml   (contents, props changed)
   incubator/directory/eve/branches/start/default.properties
   incubator/directory/eve/branches/start/docs/
   incubator/directory/eve/branches/start/docs/design/
   incubator/directory/eve/branches/start/docs/issues/
   incubator/directory/eve/branches/start/docs/issues/normalization.txt   (contents, props changed)
   incubator/directory/eve/branches/start/maven.xml   (contents, props changed)
   incubator/directory/eve/branches/start/project.properties
   incubator/directory/eve/branches/start/project.xml   (contents, props changed)
   incubator/directory/eve/branches/start/sample.ant.properties
   incubator/directory/eve/branches/start/sample.build.properties
   incubator/directory/eve/branches/start/src/
   incubator/directory/eve/branches/start/src/antlr/
   incubator/directory/eve/branches/start/src/antlr/schema.g
   incubator/directory/eve/branches/start/src/conf/
   incubator/directory/eve/branches/start/src/conf/assembly.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/conf/config.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/conf/environment.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/
   incubator/directory/eve/branches/start/src/docbook/design/
   incubator/directory/eve/branches/start/src/docbook/design/authentication-module.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/backend-module.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/client-module.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/controls.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/decoder-module.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/encoder-module.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/event-module.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/input-module.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/jndi-provider-module.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/listener-module.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/module-implementation-template.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/nexus-module.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/operations/
   incubator/directory/eve/branches/start/src/docbook/design/operations/opertion-template.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/operations/search-operation.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/output-module.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/protocol-module.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/request-session-scopes.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/rootdse-design.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/schema-module.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/seda-implementation.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/server-architecture.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/docbook/design/triggers-and-procedures.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/
   incubator/directory/eve/branches/start/src/images/AddRequestProcessorProcess.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/AdminSchemaContext.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/BackendInterfaces.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/BindRequestProcessorProcess.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/CRUDLifecycle.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/CompareRequestProcessorProcess.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/DelRequestProcessorProcess.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/ExtendedRequestProcessorProcess.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/InputEventHandlerHandleEvent.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/InputModuleRegister.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/ListenerModuleRun.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/ModifyDNRequestProcessorProcess.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/ModifyRequestProcessorProcess.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/ModuleDependencies.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/OutputEventHandlerHandleEvent.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/OutputModuleWrite.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/ReqRespStageFlow.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/RequestEventHandlerHandleEvent.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/ResponseEventHandlerHandleEvent.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/SearchRequestProcessorProcess.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/images/UnBindRequestProcessorProcess.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/
   incubator/directory/eve/branches/start/src/java/org/
   incubator/directory/eve/branches/start/src/java/org/apache/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/AbstractModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/Kernel.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/Module.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/AtomicBackend.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/AttributeAdapter.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/AttributesAdapter.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/Backend.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/BackendConfig.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/BackendException.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/BackendModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/Cursor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/CursorEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/CursorException.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/CursorListener.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/EmptyCursor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/EnumerationCursor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/IteratorCursor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/LdapEntry.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/NexusModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/NormalizerComponent.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/ReadOnlyException.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/RootDSE.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/SingletonCursor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/UnifiedBackend.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/backendhowto.html
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/contract.html
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/dnnormalization.html
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/Database.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/EntryCursor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/IndexNotFoundException.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/JdbmDatabase.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/JdbmModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/LdapEntryImpl.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/ASTNode.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/AboutDialog.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/AnnotatedFilterTreeDialog.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/BackendFrame.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/EntryNode.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/FilterDialog.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/IndexDialog.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/JdbmBackendViewer.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/JdbmTableModel.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/MultiMapModel.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/SearchResultDialog.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/ldapd.gif   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/BigIntegerComparator.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/Index.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/IndexComparator.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/IndexCursor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/IndexRecord.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/JdbmIndex.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/StringComparator.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/Assertion.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/DefaultOptimizer.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/DisjunctionCursor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/Optimizer.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/PrefetchCursor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/SearchEngine.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/DupsCursor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/JdbmTable.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/KeyOnlyComparator.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/MasterTable.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/NoDupsCursor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/Table.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/TableComparator.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/TupleComparator.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/TupleIteratorCursor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/TupleRenderer.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/package.html
   incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/sql/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/client/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientException.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientKey.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientManager.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientManagerSlave.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientSession.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/client/KeyExpiryException.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/client/LdapClientSession.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ServerConfig.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/decoder/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/decoder/Decoder.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/decoder/DecoderException.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/decoder/DecoderModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/encoder/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/encoder/Encoder.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/encoder/EncoderException.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/encoder/EncoderModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/AbstractEventHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/AuthenticationEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/AuthenticationListener.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/ClientEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/ClientListener.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/ConnectEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/ConnectListener.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/EntryEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/EntryEventSource.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/EntryListener.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/EventHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/InputEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/InputListener.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/OutputEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/OutputListener.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/RequestEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/RequestListener.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/ResponseEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/ResponseListener.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/AbandonEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/AddEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/BindEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/CompareEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/DelEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/EventManager.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/EventModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/ExtendedEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/ModifyDnEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/ModifyEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/ProtocolAdapter.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/ProtocolEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/ProtocolListener.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/SearchEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/event/protocol/UnbindEvent.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/input/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/input/InputManager.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/input/InputModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/jndi/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/jndi/BindingEnumeration.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/jndi/ContextHelper.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/jndi/DirContextHelper.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/jndi/JndiProvider.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/jndi/JndiProviderModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/jndi/NameClassPairEnumeration.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/jndi/SearchResultEnumeration.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/jndi/ServerContextFactory.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/jndi/UnifiedContext.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/jndi/UnifiedDirContext.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/jndi/UnifiedLdapContext.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/listener/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/listener/ListenerException.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/listener/ListenerModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/listener/ServerListener.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/output/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/output/OutputManager.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/output/OutputModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/AbandonHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/AddHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/BindHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/CompareHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/DeleteHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/ExtendedHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/HandlerTypeEnum.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/ModifyDNRequestProcessor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/ModifyDnHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/ModifyHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/NoReplyHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/ProtocolEngine.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/ProtocolException.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/ProtocolModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/RequestHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/SearchHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/SearchRequestProcessor.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/SingleReplyHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/Stats.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/UnbindHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/Utils.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/event/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/extended/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/protocol/extended/PayloadHandler.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/schema/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/schema/AttributeSpec.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/schema/Normalizer.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/schema/ObjectClassSpec.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/schema/ParserSyntaxChecker.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/schema/RegexNormalizer.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/schema/RegexSyntaxChecker.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/schema/Schema.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/schema/SchemaImpl.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/schema/SchemaManager.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/schema/SchemaModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/schema/SyntaxChecker.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/security/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/security/LdapPrincipal.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/security/auth/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/security/auth/AuthenticationException.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/security/auth/AuthenticationManager.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/security/auth/AuthenticationModule.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/seda/
   incubator/directory/eve/branches/start/src/java/org/apache/eve/seda/AbstractStage.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/seda/EnqueuePredicate.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/java/org/apache/eve/seda/Stage.java   (contents, props changed)
   incubator/directory/eve/branches/start/src/ldif/
   incubator/directory/eve/branches/start/src/ldif/addAll.ldif   (contents, props changed)
   incubator/directory/eve/branches/start/src/ldif/addRoot.ldif   (contents, props changed)
   incubator/directory/eve/branches/start/src/ldif/addTestUser.ldif   (contents, props changed)
   incubator/directory/eve/branches/start/src/ldif/example.ldif   (contents, props changed)
   incubator/directory/eve/branches/start/src/ldif/ori_example.ldif   (contents, props changed)
   incubator/directory/eve/branches/start/src/ldif/smallest_example.ldif   (contents, props changed)
   incubator/directory/eve/branches/start/src/schema/
   incubator/directory/eve/branches/start/src/schema/corba.schema
   incubator/directory/eve/branches/start/src/schema/core.schema
   incubator/directory/eve/branches/start/src/schema/core.xml   (contents, props changed)
   incubator/directory/eve/branches/start/src/schema/cosine.schema
   incubator/directory/eve/branches/start/src/schema/inetorgperson.schema
   incubator/directory/eve/branches/start/src/schema/java.schema
   incubator/directory/eve/branches/start/src/schema/krb5-kdc.schema
   incubator/directory/eve/branches/start/src/schema/misc.schema
   incubator/directory/eve/branches/start/src/schema/nis.schema
   incubator/directory/eve/branches/start/src/schema/openldap.schema
   incubator/directory/eve/branches/start/src/schema/our.schema
   incubator/directory/eve/branches/start/src/schema/vendor.schema
   incubator/directory/eve/branches/start/src/xdocs/
   incubator/directory/eve/branches/start/src/xdocs/images/
   incubator/directory/eve/branches/start/src/xdocs/images/design/
   incubator/directory/eve/branches/start/todo.txt   (contents, props changed)
   incubator/directory/eve/branches/start/xdocs/
   incubator/directory/eve/branches/start/xdocs/design/
   incubator/directory/eve/branches/start/xdocs/design/operations/
   incubator/directory/eve/branches/start/xdocs/design/operations/index.xml   (contents, props changed)
   incubator/directory/eve/branches/start/xdocs/design/operations/navigation.xml   (contents, props changed)
   incubator/directory/eve/branches/start/xdocs/devprocess.xml   (contents, props changed)
   incubator/directory/eve/branches/start/xdocs/index.xml   (contents, props changed)
   incubator/directory/eve/branches/start/xdocs/navigation.xml   (contents, props changed)
   incubator/directory/eve/branches/start/xdocs/stylesheets/
   incubator/directory/eve/branches/start/xdocs/stylesheets/tigris.css
   incubator/directory/eve/branches/start/xdocs/temp.xml   (contents, props changed)
Log:
Initial Eve State on SVN Import - A MAINTENANCE BRANCH ONLY FOR BUG FIXES

Added: incubator/directory/eve/branches/start/.classpath
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/.classpath	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<classpath>
+  <classpathentry kind="src" path="src/java">
+  </classpathentry>
+  <classpathentry kind="var" rootpath="JRE_SRCROOT" path="JRE_LIB" sourcepath="JRE_SRC">
+  </classpathentry>
+  <classpathentry kind="var" path="MAVEN_REPO/commons-logging/jars/commons-logging-1.0.2.jar">
+  </classpathentry>
+  <classpathentry kind="var" path="MAVEN_REPO/oro/jars/oro-2.0.7.jar">
+  </classpathentry>
+  <classpathentry kind="var" path="MAVEN_REPO/cornerstone-threads/jars/cornerstone-threads-impl-SNAPSHOT.jar">
+  </classpathentry>
+  <classpathentry kind="var" path="MAVEN_REPO/cornerstone-threads/jars/cornerstone-threads-api-SNAPSHOT.jar">
+  </classpathentry>
+  <classpathentry kind="var" path="MAVEN_REPO/excalibur-thread/jars/excalibur-thread-1.1.1.jar">
+  </classpathentry>
+  <classpathentry kind="var" path="MAVEN_REPO/avalon-phoenix/jars/avalon-phoenix-client-4.0.jar">
+  </classpathentry>
+  <classpathentry kind="var" path="MAVEN_REPO/ldapd-common/jars/ldapd-common-SNAPSHOT.jar">
+  </classpathentry>
+  <classpathentry kind="var" path="MAVEN_REPO/ldapd-snacc-provider/jars/ldapd-snacc-provider-SNAPSHOT.jar">
+  </classpathentry>
+  <classpathentry kind="var" path="MAVEN_REPO/commons-collections/jars/commons-collections-2.1.jar">
+  </classpathentry>
+  <classpathentry kind="var" path="MAVEN_REPO/regexp/jars/regexp-1.2.jar">
+  </classpathentry>
+  <classpathentry kind="var" path="MAVEN_REPO/antlr/jars/antlr-2.7.2.jar">
+  </classpathentry>
+  <classpathentry kind="var" path="MAVEN_REPO/avalon-framework/jars/avalon-framework-api-SNAPSHOT.jar">
+  </classpathentry>
+  <classpathentry kind="var" path="MAVEN_REPO/avalon-framework/jars/avalon-framework-impl-SNAPSHOT.jar">
+  </classpathentry>
+  <classpathentry kind="output" path="target/classes">
+  </classpathentry>
+</classpath>
\ No newline at end of file

Added: incubator/directory/eve/branches/start/.cvsignore
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/.cvsignore	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,22 @@
+*.twr
+*.tpr
+*.tws
+*.log
+*.db
+.nbattrs
+antlr*
+test*
+target
+dist
+build
+diagrams
+testdb*
+classes
+together
+build.properties
+together/newton/diagrams
+*javadocs
+SchemaParser.java
+SchemaSyntaxLexer.java
+SchemaTokenTypes.java
+SchemaTokenTypes.txt

Added: incubator/directory/eve/branches/start/.project
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/.project	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<projectDescription>
+  <name>ldapd-server</name>
+  <comment>Common LDAP packages used for protocol compliant parsing of distinguished names, LDIFs, filters, and urls. Also contains the Common Message API which enables a plugable interface for ASN.1 BER Message providers.</comment>
+  <projects>
+  </projects>
+  <buildSpec>
+    <buildCommand>
+      <name>org.eclipse.jdt.core.javabuilder</name>
+      <arguments>
+      </arguments>
+    </buildCommand>
+  </buildSpec>
+  <natures>
+    <nature>org.eclipse.jdt.core.javanature</nature>
+  </natures>
+</projectDescription>
\ No newline at end of file

Added: incubator/directory/eve/branches/start/ChangeLog.txt
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/ChangeLog.txt	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,17 @@
+Release 0.71a
+=============
+
+* Refactored ClientKeys to throw KeyExpiryExceptions.
+* Modified OutputModule to handle expirations and it now buffers client writes.
+* Enabled use of IO lock objects in InputModule and DecoderModule.
+* InputModule's InputStreamMonitor now politely exits when the ClientKey is
+  expired rather than exiting due to an IOException on the InputStream.
+* Cleaned up the ClientManager interface and its ClientModule implementation
+  to enable named and anonymous sessions.
+* ClientModule now dependent on Nexus which has become a ClientManagerSlave
+  along with all backends.
+* ProtocolEngine now uses ClientManager interface methods to assiciate worker
+  threads with clients when they process requests on behalf of the client.
+* Added ability to transparently inject or alter operational attributes within
+  the nexus module.
+

Added: incubator/directory/eve/branches/start/LICENSE.txt
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/LICENSE.txt	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,204 @@
+/*
+ *                                 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.
+ *
+ */

Added: incubator/directory/eve/branches/start/SearchProblems.txt
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/SearchProblems.txt	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,59 @@
+            Problems With The Search Operation
+--------------------------------------------------------------
+
+* The following search parameters are not taken into account:
+
+    - timeLimit
+    - sizeLimit
+    - typesOnly
+    - derefAliases
+
+* Pruning of undefined filter assertions does not occur.
+  This must happen to facilitate the behavoir defined for
+  searches with invalid filter expressions in accordance
+  with RFC 2251.
+
+* Alias dereferencing obviously from the list above is not 
+  supported at the present moment.
+
+* Referals are not supported at all by the search processor
+  at the present moment.
+
+* The search entry responses do not add binary flags in the 
+  attribute description for binary attributes.
+
+* Note that all requests except for abandon and unbind (which
+  do not return responses) can return a referal to find the 
+  target entry.  Only the search requests manage the resolution
+  of referals and aliases.
+
+* Extensible matching is not enabled.
+
+* Undefined filter assertion pruning is not performed.
+
+* The attribute list to return is not supported and all 
+  including operational attributes are returned.  We need to
+  return all attributes (but not operational attributes) if 
+  the attribute list is empty or has a "*" wild card (which 
+  returns operational attributes as well.
+
+* Need to recognize the special reserved "1.1" attribute OID 
+  in the attribute list to connotate the return of none of the 
+  attributes in the entry.
+
+
+
+                   Other Protocol Violations
+--------------------------------------------------------------
+
+* Notice of Disconnect not implemented.
+
+* Referal error not returned when base Dn of operation is a 
+  referal.
+
+* LDAP URLs are not supported with the '%' escape syntax of 
+  RFC 1738.
+
+* Control criticality is not checked on unrecognized controls.
+
+

Added: incubator/directory/eve/branches/start/build.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/build.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,759 @@
+<?xml version="1.0"?>
+<project name="eve" default="sars" basedir=".">
+
+	<!-- ============================================================
+                           PROPERTY SETUP
+  ============================================================= -->
+
+	<!-- load properties files -->
+	<property file="${user.home}/build.properties"/>
+	<property file="${user.home}/.ant.properties"/>
+	<property file="${basedir}/ant.properties"/>
+	<property file="${basedir}/../ant.properties"/>
+	<property file="${basedir}/default.properties"/>
+	<property file="${basedir}/../default.properties"/>
+	<property file="${basedir}/../cocoon.properties"/>
+
+	<!-- Set the properties for intermediate directory -->
+	<property name="build.dir" value="build"/>
+	<property name="build.lib" value="${build.dir}/lib"/>
+	<property name="build.xdoclet" value="${build.dir}/xdoclet"/>
+	<property name="build.src" value="${build.dir}/src"/>
+	<property name="build.classes" value="${build.dir}/classes"/>
+	<property name="build.javadocs" value="${build.dir}/javadocs"/>
+	<property name="build.docs" value="${build.dir}/docs"/>
+	<property name="build.context" value="${build.dir}/documentation"/>
+	<property name="build.xdocs" value="${build.context}/content/xdocs"/>
+
+	<!-- Set the properties for source directories -->
+	<property name="src.dir" value="src"/>
+	<property name="java.dir" value="${src.dir}/java"/>
+	<property name="manifest.dir" value="${src.dir}/manifest"/>
+	<property name="conf.dir" value="${src.dir}/conf"/>
+	<property name="xdocs.dir" value="${src.dir}/xdocs"/>
+	<property name="dist.base" value="distributions"/>
+  <property name="remote.repo" value="http://cvs.apache.org/repository"/>
+
+  <property environment="env"/>
+
+  <!-- ============================================================
+                   DEPENDENCY JARS PROPERTY SETUP
+  ============================================================= -->
+
+  <property
+    name="ldap-common.jar"
+    value="ldap-common-SNAPSHOT.jar"/>
+
+	<property
+    name="commons-collections.jar"
+    value="commons-collections-2.1.jar"/>
+
+	<property
+    name="commons-lang.jar"
+    value="commons-lang-2.0.jar"/>
+
+	<property
+    name="snacc.jar"
+    value="snacc-2.3.jar"/>
+
+	<property
+    name="antlr.jar"
+    value="antlr-2.7.2.jar"/>
+
+	<property
+    name="avalon-framework-api.jar"
+    value="avalon-framework-api-4.1.5.jar"/>
+
+  <property
+    name="avalon-framework-impl.jar"
+    value="avalon-framework-impl-4.1.5.jar"/>
+
+  <property
+    name="regexp.jar"
+    value="regexp-1.2.jar"/>
+
+  <property
+    name="ldap-snacc-provider.jar"
+    value="ldap-snacc-provider-SNAPSHOT.jar"/>
+
+  <property
+    name="oro.jar"
+    value="oro-2.0.7.jar"/>
+
+  <property
+    name="cornerstone-threads-api.jar"
+    value="cornerstone-threads-api-2.0.jar" />
+
+  <property
+    name="cornerstone-threads-impl.jar"
+    value="cornerstone-threads-impl-2.0.jar" />
+
+  <property
+    name="excalibur-thread.jar"
+    value="excalibur-thread-1.1.1.jar"/>
+
+  <property
+    name="jdbm.jar"
+    value="jdbm-0.12.jar"/>
+
+  <property
+    name="logkit.jar"
+    value="logkit-1.2.jar"/>
+
+  <!-- ============================================================
+                      TOOLS JARS PROPERTY SETUP
+  ============================================================= -->
+
+  <property
+    name="xdoclet.jar"
+    value="xdoclet-20020825.jar"/>
+
+  <property
+    name="xjavadoc.jar"
+    value="xjavadoc-20020825.jar"/>
+
+  <property
+    name="phoenix-client.jar"
+    value="avalon-phoenix-client-4.0.4.jar"/>
+
+  <property
+    name="commons-logging.jar"
+    value="commons-logging-1.0.3.jar"/>
+
+  <property
+    name="log4j.jar"
+    value="log4j-1.2.7.jar"/>
+
+  <property
+    name="velocity.jar"
+    value="velocity-1.3.jar"/>
+
+  <property
+    name="jdom.jar"
+    value="jdom-b7.jar"/>
+
+  <!-- ============================================================
+                           CLASSPATH SETUP
+  ============================================================= -->
+
+	<!-- Set some class paths -->
+	<path id="project.class.path">
+		<pathelement path="${java.class.path}"/>
+		<pathelement path="${build.classes}"/>
+		<pathelement path="${build.lib}/${ldap-common.jar}"/>
+		<pathelement path="${build.lib}/${commons-lang.jar}"/>
+		<pathelement path="${build.lib}/${snacc.jar}"/>
+		<pathelement path="${build.lib}/${antlr.jar}"/>
+		<pathelement path="${build.lib}/${avalon-framework-api.jar}"/>
+		<pathelement path="${build.lib}/${avalon-framework-impl.jar}"/>
+		<pathelement path="${build.lib}/${commons-collections.jar}"/>
+		<pathelement path="${build.lib}/${regexp.jar}"/>
+		<pathelement path="${build.lib}/${oro.jar}"/>
+		<pathelement path="${build.lib}/${cornerstone-threads-api.jar}"/>
+		<pathelement path="${build.lib}/${cornerstone-threads-impl.jar}"/>
+		<pathelement path="${build.lib}/${phoenix-client.jar}"/>
+    <pathelement path="${build.lib}/${excalibur-thread.jar}"/>
+    <pathelement path="${build.lib}/${jdbm.jar}"/>
+    <pathelement path="${build.lib}/${logkit.jar}"/>
+	</path>
+
+	<path id="tools.class.path">
+		<path refid="project.class.path"/>
+    <pathelement location="${build.lib}/${log4j.jar}"/>
+    <pathelement location="${build.lib}/${commons-logging.jar}"/>
+    <pathelement location="${build.lib}/${xdoclet.jar}"/>
+    <pathelement location="${build.lib}/${xjavadoc.jar}"/>
+    <pathelement location="${build.lib}/${phoenix-client.jar}"/>
+    <pathelement location="${build.lib}/${velocity.jar}"/>
+    <pathelement location="${build.lib}/${jdom.jar}"/>
+	</path>
+
+
+  <target name="get-deps">
+    <mkdir dir="${build.lib}"/>
+
+    <!-- ======================================================================
+                              GET TOOL DEPENDENCY JARS
+    ======================================================================= -->
+
+    <get
+      usetimestamp="true"
+      src="${remote.repo}/log4j/jars/${log4j.jar}"
+      dest="${build.lib}/${log4j.jar}"
+      verbose="true"
+      />
+    <get
+      usetimestamp="true"
+      src="${remote.repo}/commons-logging/jars/${commons-logging.jar}"
+      dest="${build.lib}/${commons-logging.jar}"
+      verbose="true"
+      />
+    <get
+      usetimestamp="true"
+      src="${remote.repo}/xdoclet/jars/${xdoclet.jar}"
+      dest="${build.lib}/${xdoclet.jar}"
+      verbose="true"
+      />
+    <get
+      usetimestamp="true"
+      src="${remote.repo}/xjavadoc/jars/${xjavadoc.jar}"
+      dest="${build.lib}/${xjavadoc.jar}"
+      verbose="true"
+      />
+    <get
+      usetimestamp="true"
+      src="${remote.repo}/avalon-phoenix/jars/${phoenix-client.jar}"
+      dest="${build.lib}/${phoenix-client.jar}"
+      verbose="true"
+      />
+    <get
+      usetimestamp="true"
+      src="${remote.repo}/velocity/jars/${velocity.jar}"
+      dest="${build.lib}/${velocity.jar}"
+      verbose="true"
+      />
+    <get
+      usetimestamp="true"
+      src="${remote.repo}/jdom/jars/${jdom.jar}"
+      dest="${build.lib}/${jdom.jar}"
+      verbose="true"
+      />
+
+    <!-- ======================================================================
+                                GET DEPENDENCY JARS
+    ======================================================================= -->
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/incubator-directory/jars/${ldap-common.jar}"
+      dest="${build.lib}/${ldap-common.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/incubator-directory/jars/${ldap-snacc-provider.jar}"
+      dest="${build.lib}/${ldap-snacc-provider.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/commons-lang/jars/${commons-lang.jar}"
+      dest="${build.lib}/${commons-lang.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="http://ldapd.sourceforge.net/maven/repository/snacc4j/jars/${snacc.jar}"
+      dest="${build.lib}/${snacc.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/antlr/jars/${antlr.jar}"
+      dest="${build.lib}/${antlr.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/avalon-framework/jars/${avalon-framework-api.jar}"
+      dest="${build.lib}/${avalon-framework-api.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/avalon-framework/jars/${avalon-framework-impl.jar}"
+      dest="${build.lib}/${avalon-framework-impl.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/commons-collections/jars/${commons-collections.jar}"
+      dest="${build.lib}/${commons-collections.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/regexp/jars/${regexp.jar}"
+      dest="${build.lib}/${regexp.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/oro/jars/${oro.jar}"
+      dest="${build.lib}/${oro.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/cornerstone-threads/jars/${cornerstone-threads-api.jar}"
+      dest="${build.lib}/${cornerstone-threads-api.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/cornerstone-threads/jars/${cornerstone-threads-impl.jar}"
+      dest="${build.lib}/${cornerstone-threads-impl.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/excalibur-thread/jars/${excalibur-thread.jar}"
+      dest="${build.lib}/${excalibur-thread.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/jdbm/jars/${jdbm.jar}"
+      dest="${build.lib}/${jdbm.jar}"
+      />
+
+    <get verbose="true" usetimestamp="true"
+      src="${remote.repo}/logkit/jars/${logkit.jar}"
+      dest="${build.lib}/${logkit.jar}"
+      />
+  </target>
+
+	<!-- ======================================================
+
+
+			       T A R G E T S
+
+
+	======================================================= -->
+
+
+	<!-- ======================================================
+			    C O M P I L A T I O N
+	======================================================= -->
+
+	
+	<target name="grammars" depends="get-deps">
+		<antlr target="src/antlr/schema.g" 
+			   outputdirectory="src/java/org/apache/eve/schema">
+			<classpath refid="tools.class.path"/>
+		</antlr>
+	</target>
+
+
+	<target name="compile" depends="grammars">
+		<mkdir dir="${build.classes}"/>
+
+		<javac srcdir="${java.dir}"
+      destdir="${build.classes}"
+      debug="${build.debug}"
+      optimize="${build.optimize}"
+      deprecation="${build.deprecation}">
+			<classpath refid="project.class.path"/>
+		</javac>
+
+		<copy todir="${build.classes}">
+			<fileset dir="${java.dir}">
+				<exclude name="**/test/**"/>
+				<exclude name="**/*.java"/>
+			</fileset>
+		</copy>
+	</target>
+
+
+	<!-- ======================================================
+	X I N F O   A N D   M A N I F E S T   G E N E R A T I O N 
+	======================================================= -->
+	
+	<target name="test-xdoclet">
+		<available classname="xdoclet.XDocletMain"
+      classpathref="tools.class.path"
+      property="xdoclet.present"/>
+	</target>
+	
+	<target name="phoenix-xdoclet" depends="compile" ><!--if="xdoclet.present"-->
+		<mkdir dir="${build.xdoclet}"/>
+		<taskdef
+      name="phoenix-blocks"
+      classname="org.apache.avalon.phoenix.tools.xdoclet.PhoenixXDoclet"
+      classpathref="tools.class.path"/>
+
+		<phoenix-blocks destdir="${build.xdoclet}">
+			<fileset dir="${java.dir}">
+				<include name="org/apache/eve/event/protocol/*.java"/>
+			</fileset>
+			<blockinfo/>
+			<mxinfo/>
+		</phoenix-blocks>
+
+		<phoenix-blocks destdir="${build.xdoclet}">
+			<fileset dir="${java.dir}">
+				<include name="org/apache/eve/jndi/*.java"/>
+			</fileset>
+			<blockinfo/>
+			<mxinfo/>
+		</phoenix-blocks>
+
+		<phoenix-blocks destdir="${build.xdoclet}">
+			<fileset dir="${java.dir}">
+				<include name="org/apache/eve/input/*.java"/>
+			</fileset>
+			<blockinfo/>
+			<mxinfo/>
+		</phoenix-blocks>
+
+		<phoenix-blocks destdir="${build.xdoclet}">
+			<fileset dir="${java.dir}">
+				<include name="org/apache/eve/output/*.java"/>
+			</fileset>
+			<blockinfo/>
+			<mxinfo/>
+		</phoenix-blocks>
+
+		<phoenix-blocks destdir="${build.xdoclet}">
+			<fileset dir="${java.dir}">
+				<include name="org/apache/eve/encoder/*.java"/>
+			</fileset>
+			<blockinfo/>
+			<mxinfo/>
+		</phoenix-blocks>
+
+		<phoenix-blocks destdir="${build.xdoclet}">
+			<fileset dir="${java.dir}">
+				<include name="org/apache/eve/security/auth/*.java"/>
+			</fileset>
+			<blockinfo/>
+			<mxinfo/>
+		</phoenix-blocks>
+
+		<phoenix-blocks destdir="${build.xdoclet}">
+			<fileset dir="${java.dir}">
+				<include name="org/apache/eve/protocol/*.java"/>
+			</fileset>
+			<blockinfo/>
+			<mxinfo/>
+		</phoenix-blocks>
+
+		<phoenix-blocks destdir="${build.xdoclet}">
+			<fileset dir="${java.dir}">
+				<include name="org/apache/eve/decoder/*.java"/>
+			</fileset>
+			<blockinfo/>
+			<mxinfo/>
+		</phoenix-blocks>
+
+		<phoenix-blocks destdir="${build.xdoclet}">
+			<fileset dir="${java.dir}">
+				<include name="org/apache/eve/client/*.java"/>
+			</fileset>
+			<blockinfo/>
+			<mxinfo/>
+		</phoenix-blocks>
+
+		<phoenix-blocks destdir="${build.xdoclet}">
+			<fileset dir="${java.dir}">
+				<include name="org/apache/eve/schema/*.java"/>
+			</fileset>
+			<blockinfo/>
+			<mxinfo/>
+		</phoenix-blocks>
+
+		<phoenix-blocks destdir="${build.xdoclet}">
+			<fileset dir="${java.dir}">
+				<include name="org/apache/eve/listener/*.java"/>
+			</fileset>
+			<blockinfo/>
+			<mxinfo/>
+		</phoenix-blocks>
+
+		<phoenix-blocks destdir="${build.xdoclet}">
+			<fileset dir="${java.dir}">
+				<include name="org/apache/eve/backend/*.java"/>
+			</fileset>
+			<blockinfo/>
+			<mxinfo/>
+		</phoenix-blocks>
+
+    <phoenix-blocks destdir="${build.xdoclet}">
+      <fileset dir="${java.dir}">
+        <include name="org/apache/eve/backend/jdbm/*.java"/>
+      </fileset>
+      <blockinfo/>
+      <mxinfo/>
+    </phoenix-blocks>
+	</target>
+
+
+	<!-- Make .xinfo and manifest automatically for blocks -->
+	<target name="no-phoenix-xdoclet" depends="compile" unless="xdoclet.present">
+		<mkdir dir="${build.xdoclet}"/>
+		<unzip src="${src.dir}/generated-by-xdoclet.zip" dest="${build.xdoclet}"/>
+	</target>
+
+	<!-- ======================================================
+					        P R O J E C T    J A R S
+	======================================================= -->
+
+	<target name="jars" depends="phoenix-xdoclet">
+    <!--depends="test-xdoclet, phoenix-xdoclet, no-phoenix-xdoclet"-->
+		<mkdir dir="${build.lib}"/>
+
+		<!-- Just jars the individual block packages not the required set of classes 
+			 the core classes need to be jared into a separate core.jar.
+		-->
+
+		<jar jarfile="${build.lib}/core.jar" basedir="${build.classes}">
+			<!-- Include entire package -->
+			<include name="org/apache/eve/**"/>
+
+			<!-- Exclude the blocks jared below -->
+			<exclude name="org/apache/eve/input/*"/>
+			<exclude name="org/apache/eve/output/*"/>
+      <exclude name="org/apache/eve/encoder/*"/>
+			<exclude name="org/apache/eve/event/protocol/*"/>
+			<exclude name="org/apache/eve/security/auth/*"/>
+			<exclude name="org/apache/eve/protocol/*"/>
+			<exclude name="org/apache/eve/decoder/*"/>
+			<exclude name="org/apache/eve/client/*"/>
+			<exclude name="org/apache/eve/schema/*"/>
+			<exclude name="org/apache/eve/listener/*"/>
+			<exclude name="org/apache/eve/backend/*"/>
+		</jar>
+
+		<jar jarfile="${build.lib}/event.jar" basedir="${build.classes}">
+			<include name="org/apache/eve/event/protocol/*"/>
+			<fileset dir="${build.xdoclet}">
+				<include name="org/apache/eve/event/protocol/*.xinfo"/>
+				<include name="org/apache/eve/event/protocol/*.mxinfo"/>
+			</fileset>
+		</jar>
+
+		<jar jarfile="${build.lib}/jndi.jar" basedir="${build.classes}">
+			<include name="org/apache/eve/jndi/*"/>
+			<fileset dir="${build.xdoclet}">
+				<include name="org/apache/eve/jndi/*.xinfo"/>
+				<include name="org/apache/eve/jndi/*.mxinfo"/>
+			</fileset>
+		</jar>
+
+		<jar jarfile="${build.lib}/input.jar" basedir="${build.classes}">
+			<include name="org/apache/eve/input/*"/>
+			<fileset dir="${build.xdoclet}">
+				<include name="org/apache/eve/input/*.xinfo"/>
+				<include name="org/apache/eve/input/*.mxinfo"/>
+			</fileset>
+		</jar>
+
+		<jar jarfile="${build.lib}/output.jar" basedir="${build.classes}">
+			<include name="org/apache/eve/output/*"/>
+			<fileset dir="${build.xdoclet}">
+				<include name="org/apache/eve/output/*.xinfo"/>
+				<include name="org/apache/eve/output/*.mxinfo"/>
+			</fileset>
+		</jar>
+
+		<jar jarfile="${build.lib}/encoder.jar" basedir="${build.classes}">
+			<include name="org/apache/eve/encoder/*"/>
+			<fileset dir="${build.xdoclet}">
+				<include name="org/apache/eve/encoder/*.xinfo"/>
+				<include name="org/apache/eve/encoder/*.mxinfo"/>
+			</fileset>
+		</jar>
+
+		<jar jarfile="${build.lib}/authman.jar" basedir="${build.classes}">
+			<include name="org/apache/eve/security/auth/*"/>
+			<fileset dir="${build.xdoclet}">
+				<include name="org/apache/eve/security/auth/*.xinfo"/>
+				<include name="org/apache/eve/security/auth/*.mxinfo"/>
+			</fileset>
+		</jar>
+
+		<jar jarfile="${build.lib}/protocol.jar" basedir="${build.classes}">
+			<include name="org/apache/eve/protocol/*"/>
+			<fileset dir="${build.xdoclet}">
+				<include name="org/apache/eve/protocol/*.xinfo"/>
+				<include name="org/apache/eve/protocol/*.mxinfo"/>
+			</fileset>
+		</jar>
+
+		<jar jarfile="${build.lib}/decoder.jar" basedir="${build.classes}">
+			<include name="org/apache/eve/decoder/*"/>
+			<fileset dir="${build.xdoclet}">
+				<include name="org/apache/eve/decoder/*.xinfo"/>
+				<include name="org/apache/eve/decoder/*.mxinfo"/>
+			</fileset>
+		</jar>
+
+		<jar jarfile="${build.lib}/client.jar" basedir="${build.classes}">
+			<include name="org/apache/eve/client/*"/>
+			<fileset dir="${build.xdoclet}">
+				<include name="org/apache/eve/client/*.xinfo"/>
+				<include name="org/apache/eve/client/*.mxinfo"/>
+			</fileset>
+		</jar>
+
+		<jar jarfile="${build.lib}/listener.jar" basedir="${build.classes}">
+			<include name="org/apache/eve/listener/*"/>
+			<fileset dir="${build.xdoclet}">
+				<include name="org/apache/eve/listener/*.xinfo"/>
+				<include name="org/apache/eve/listener/*.mxinfo"/>
+			</fileset>
+		</jar>
+
+		<jar jarfile="${build.lib}/schema.jar" basedir="${build.classes}">
+			<include name="org/apache/eve/schema/*"/>
+			<fileset dir="${build.xdoclet}">
+				<include name="org/apache/eve/schema/*.xinfo"/>
+				<include name="org/apache/eve/schema/*.mxinfo"/>
+			</fileset>
+		</jar>
+
+		<jar jarfile="${build.lib}/backend.jar" basedir="${build.classes}">
+			<include name="org/apache/eve/backend/*"/>
+			<fileset dir="${build.xdoclet}">
+				<include name="org/apache/eve/backend/*.xinfo"/>
+				<include name="org/apache/eve/backend/*.mxinfo"/>
+			</fileset>
+		</jar>
+
+    <jar jarfile="${build.lib}/backjdbm.jar" basedir="${build.classes}">
+      <include name="org/apache/eve/backend/jdbm/**"/>
+      <fileset dir="${build.xdoclet}">
+        <include name="org/apache/eve/backend/jdbm/*.xinfo"/>
+        <include name="org/apache/eve/backend/jdbm/*.mxinfo"/>
+      </fileset>
+    </jar>
+	</target>
+
+
+	<!-- ======================================================
+					        C O R E    T A R G E T S
+	======================================================= -->
+
+	<target name="sars" depends="jars">
+    <taskdef name="sar" classname="org.apache.avalon.phoenix.tools.tasks.Sar">
+      <classpath refid="tools.class.path"/>
+    </taskdef>
+
+    <taskdef name="anakia" classname="org.apache.velocity.anakia.AnakiaTask">
+      <classpath refid="tools.class.path"/>
+    </taskdef>
+
+    <sar sarfile="${build.lib}/eve.sar"
+			config="${conf.dir}/config.xml" 
+			environment="${conf.dir}/environment.xml" 
+			assembly="${conf.dir}/assembly.xml">
+
+			<lib dir="src">
+				<include name="schema/**.schema"/>
+			</lib>
+
+			<lib dir="${build.lib}/">
+				<include name="${antlr.jar}"/>
+        <include name="authman.jar"/>
+        <include name="${avalon-framework-api.jar}"/>
+        <include name="${avalon-framework-impl.jar}"/>
+        <include name="${phoenix-client.jar}"/>
+        <include name="backend.jar"/>
+        <include name="backjdbm.jar"/>
+        <include name="client.jar"/>
+        <include name="${commons-collections.jar}"/>
+        <include name="${commons-lang.jar}"/>
+        <include name="${commons-logging.jar}"/>
+        <include name="core.jar"/>
+        <include name="${cornerstone-threads-api.jar}"/>
+        <include name="${cornerstone-threads-impl.jar}"/>
+        <include name="decoder.jar"/>
+        <include name="encoder.jar"/>
+				<include name="event.jar"/>
+        <include name="${excalibur-thread.jar}"/>
+        <include name="input.jar"/>
+        <include name="${jdbm.jar}"/>
+				<include name="jndi.jar"/>
+        <include name="${ldap-common.jar}"/>
+        <include name="${ldap-snacc-provider.jar}"/>
+				<include name="listener.jar"/>
+        <include name="${logkit.jar}"/>
+				<include name="${oro.jar}"/>
+        <include name="output.jar"/>
+        <include name="protocol.jar"/>
+        <include name="${regexp.jar}"/>
+				<include name="schema.jar"/>
+				<include name="${snacc.jar}"/>
+			</lib>
+		</sar>
+
+		<mkdir dir="dist"/>
+		<copy todir="dist">
+			<fileset dir="${build.lib}">
+				<include name="*.sar"/>
+			</fileset>
+		</copy>
+
+		<echo message="done with sars"/>
+	</target>
+
+
+	<!-- ======================================================
+				  I N S T A L L     D E P L O Y
+	======================================================= -->
+
+  <target name="findInstallDir" unless="${install.dir}">
+    <property name="phoenix.home" value="${env.PHOENIX_HOME}"/>
+    <condition property="install.dir" value="${env.PHOENIX_HOME}/apps">
+      <not>
+        <contains string="${phoenix.home}" substring="env.PHOENIX_HOME"/>
+      </not>
+    </condition>
+  </target>
+
+  <target name="doPhoenixInstall"
+    unless="install.dir" depends="findInstallDir" >
+
+    <echo message="PHOENIX_HOME NOT SET!"/>
+    <echo message="CANNOT FIND PHOENIX INSTALLATION!"/>
+    <input
+      message="Would you like to download and install Phoenix into ./dist?"
+      validargs="yes,no"
+      addproperty="install.phoenix"/>
+  </target>
+
+  <target name="installPhoenix" depends="doPhoenixInstall" if="install.phoenix">
+    <get
+      usetimestamp="true" verbose="true"
+      src="${remote.repo}/avalon-phoenix/distributions/phoenix-4.0.4-bin.zip"
+      dest="${dist.dir}/phoenix-4.0.4-bin.zip"/>
+    <unzip src="${dist.dir}/phoenix-4.0.4-bin.zip" dest="${dist.dir}" />
+    <property name="install.dir" value="${dist.dir}/phoenix-4.0.4/apps"/>
+
+    <echo message="+--------------------------------------------------------"/>
+    <echo message=": Set the environment property PHOENIX_HOME: i.e"/>
+    <echo message=": PHOENIX_HOME=${dist.dir}/phoenix-4.0.4"/>
+    <echo message=": export PHOENIX_HOME"/>
+    <echo message=":"/>
+    <echo message=": Start the server using shell scripts or bat files:"/>
+    <echo message=": ${dist.dir}/phoenix-4.0.4/bin/phoenix.sh start"/>
+    <echo message=":"/>
+    <echo message=": NOTE: On UNIX you'll need to chmod +x the shell scripts"/>
+    <echo message="+--------------------------------------------------------"/>
+  </target>
+
+	<target name="install" depends="sars,installPhoenix"
+    description="Installs into Phoenix">
+
+		<echo message="Removing older installation if any from ${install.dir}"/>
+    <delete file="${install.dir}/eve.sar"/>
+    <delete dir="${install.dir}/eve/"/>
+
+    <!-- delete older conflicting thread pool jar -->
+    <delete file="${dist.dir}/phoenix-4.0.4/lib/excalibur-thread-1.1.jar"/>
+
+    <echo message="Installing to ${install.dir}"/>
+		<copy todir="${install.dir}">
+			<fileset dir="${build.lib}">
+				<include name="eve.sar"/>
+			</fileset>
+		</copy>
+	</target>
+
+	<target name="uninstall" depends="findInstallDir"
+    description="Uninstalls from Phoenix">
+		<delete dir="${install.dir}/eve/"/>
+		<delete file="eve.sar" dir="${install.dir}"/>
+	</target>
+
+	<target name="clean" description="Cleans up artifacts from build process">
+		<delete dir="${build.dir}"/>
+		<delete dir="${dist.dir}"/>
+    <delete dir="target"/>
+		<delete dir="test"/>
+		<delete>
+			<fileset dir="." includes="**/*~" defaultexcludes="no"/>
+		</delete>
+		<delete>
+			<fileset dir="src/java/org/apache/eve/schema" 
+				includes="antlr*.*" defaultexcludes="no"/>
+		</delete>
+	</target>
+
+</project>

Added: incubator/directory/eve/branches/start/default.properties
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/default.properties	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,104 @@
+# ============================================================================
+# 				Standard Build Properties for Eve
+# ============================================================================
+# Specifies the default values for properties commonly required in ant build
+# scripts. Normally parts are overridden by ./{projectname}/default.properties
+# and/or ant.properties files. This file is not ment for editing by the 
+# average user; use ant.properties files instead
+#
+#  
+# 
+#
+
+# ----------------------------------------------------------------------------
+#           DOC GENERATION CONFIGURATION
+# ----------------------------------------------------------------------------
+# this should be overridden for each application
+
+name					= eve-server
+Name					= Eve Directory Server Project
+dir-name				= eve-server
+version					= 0.01
+package-version			= 0.01
+year					= 2002-2003
+
+# ----------------------------------------------------------------------------
+#             COMPILATION ENVIRONMENT
+# ----------------------------------------------------------------------------
+#  Settings used to configure compile environment
+
+build.debug			 	= on
+build.optimize			= off
+build.deprecation		= off
+build.compress			= false
+junit.failonerror		= false
+
+# ----------------------------------------------------------------------------
+#             DIRECTORY LOCATIONS
+# ----------------------------------------------------------------------------
+
+# ----- build destinations -----
+build.dir				= ${basedir}/build
+build.classes			= ${build.dir}/classes
+build.lib				= ${build.dir}/lib
+build.conf				= ${build.dir}/conf
+build.xdoclet			= ${build.dir}/xdoclet
+build.testsrc			= ${build.dir}/testsrc
+build.testclasses		= ${build.dir}/testclasses
+build.xdocs				= ${build.context}/content/xdocs
+build.docs				= ${build.dir}/docs
+build.javadocs			= ${build.docs}/api
+build.tests				= ${build.dir}/tests
+build.reports			= ${build.dir}/reports
+# intermediate location for documentation generation
+build.context			= ${build.dir}/documentation
+
+# ----- build sources -----
+src.dir					= ${basedir}/src
+java.dir				= ${src.dir}/java
+conf.dir				= ${src.dir}/conf
+test.dir				= ${src.dir}/test
+manifest.dir			= ${src.dir}/manifest
+xdocs.dir				= ${src.dir}/xdocs
+lib.dir					= ${basedir}/lib
+
+# ----- build distributions -----
+dist.dir				= ${basedir}/dist
+dist.javadocs			= ${dist.dir}/docs/api
+site.dir				= ${basedir}/website/build/docs/apps/
+# directory for distribution archives
+dist.base 				= ${basedir}/distributions
+
+# ----------------------------------------------------------------------------
+#                LIBRARY LOCATIONS
+# ----------------------------------------------------------------------------
+
+
+# ----------------------------------------------------------------------------
+#         REMOTE DOCUMENTATION LOCATIONS
+# ----------------------------------------------------------------------------
+# Base pointers for non-xdocs documentation. Override these in .ant.properties
+# to link to local docs
+
+avalon.base				= http://jakarta.apache.org/avalon
+framework.base			= http://jakarta.apache.org/avalon/framework
+excalibur.base			= http://jakarta.apache.org/avalon/excalibur
+phoenix.base			= http://jakarta.apache.org/avalon/phoenix
+cornerstone.base		= http://jakarta.apache.org/avalon/cornerstone
+logkit.base				= http://jakarta.apache.org/avalon/logkit
+apps.base				= http://jakarta.apache.org/avalon/apps
+testlet.base			= http://jakarta.apache.org/avalon/testlet
+
+# ----------------------------------------------------------------------------
+#                OTHER PROPERTIES
+# ----------------------------------------------------------------------------
+
+#  name of .zip/.tar.gz/.bz2 files and their top-level directory
+dist.name				= ${name}-${version}
+
+#  name of jar file
+jar.name				= ${name}-${version}.jar
+
+# which method of documetation generation to use
+documentation.tool		= cocoon
+

Added: incubator/directory/eve/branches/start/docs/issues/normalization.txt
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/docs/issues/normalization.txt	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,89 @@
+
+Problem Description:
+
+Right now normalizers are run against attribute values without considering
+some of the pathalogical forms that can be present within the value.  Schemas
+expose normalizers directly to external modules enabling them to perform
+normalization on values directly with the normalizer. This is common in
+backends that maintain their own indices or other parts of the system that
+need a cannonical representation for an attribute value.
+
+For example an attribute with a IA5CaseIgnoreMatch matching rule cannot 
+normalize regions where literal quotes are used.  Meaning it cannot lowercase 
+the literal sections or deep trim spaces from it.  It must be matched against 
+as-is even though the matching rule states otherwise.  Hence a user must put
+together filter attribute values in the same case and spacing with quotes to
+match against a literal section.  In conclusion literal value sections override matching rules.
+
+Presently cannonical representations are normalized even in literal sections
+for attribute values.  The DnParser on the otherhand correctly normalizes or
+defers normalization based on context perserving literal sections.  Indices
+on DN attributes in this respect will be inconsistent when quotes are used 
+and some searches will return more results than they should have.
+
+This potential problem is rare, depends on the data values used and requires
+the use of quotes.  It is important yet not a show stopper, but should be
+solved eventually for the sake of correctness.
+
+
+Solution:
+
+We need to parse the value and apply the normalization function associated 
+with the matching rule to non-literal sections only, concatenate normalized
+regions with literal regions and return this as the overall normalized value.
+
+So if we had an attribute 'abcxyz' where the matching rule is
+IA5CaseIgnoreMatch with the following value with boundry ticks:
+
+     1      2 3                 4 5             6
+     |      | |             | |         |
+    'cdJFAsdf \"Jack in The box\"   j AS   2345DS'
+
+would be transformed into:
+
+     1      2 3                 4 5         6
+     |      | |             | |         |
+    'cdjfAsdf \"Jack in The box\" j as 2345ds'
+
+Here segments 12 and 56 are normalized using the matching rules assigned 
+normalization funtion.  Segment 34 is untouched as a literal.  The
+transformed string is then used as an index key to lookup entries with
+this value.
+
+
+Implementation:
+
+Currently a value parser is used to correctly parse out a the values of
+DN name components optionally normalizing the values of RDN attributes 
+based on their matching rules as specified by the schema manager.  This
+parser is on in a two part parser system which together implement a 
+parser with two lexical states using a token stream switcher.  Complicating
+it further with code to merely parse a single attributes value would
+present a maintenence nightmare.  Since these parsers are antlr generated
+we can easily create a new parser which would be very simple to parse just
+attribute values which normalizing them according their matching rules
+defined in a schema.
+
+Constructing a new parser adds an extra overhead. First of all a parser 
+stores state and so must be synchronized upon.  This creates contention.
+Secondly new string objects are instantiated all over the place presenting
+a potential performance problem.  Indices manage value normalization to
+conduct searches so they can cache the most recently transformed values
+to prevent redundant normalizations accross subsystem boundries.  The same
+value may be normalized several times if it is not cached.  Also any such
+cache must be specific to an attribute to prevent value collisions in the
+cache.  Indices are the best place for caching normalized values namely 
+since the see the resultant products of normalization.  Normalizers may
+only be seeing parts of value.  In the example above a Normalizer never
+sees the complete input: it sees segments 12 and 56 without seeing segment
+34 at all.  It would store two entries total for this attribute value pair.
+The cache in an index would store one entry who's key is the original 
+value and the value is the normalized value.  This is more efficient and
+practical.
+
+Indices must then manage a cache of normalized values and use them on
+cache hits or delegate normalization requests to the schema.  The schema
+invokes a value parser with the correct normalizer for the attribute's 
+matching rule and returns the resultant normalized value.  This normalized
+value is also known as the cannonical value or simply the index key.
+

Added: incubator/directory/eve/branches/start/maven.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/maven.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,32 @@
+<project default="java:compile"
+    xmlns:j="jelly:core"
+    xmlns:u="jelly:util"
+    xmlns:ant="jelly:ant"
+    xmlns:maven="jelly:maven"
+    xmlns:m="maven"
+    xmlns:deploy="deploy">
+    
+    <preGoal name="site">
+        <attainGoal name="docbook:transform"/>
+    </preGoal>
+
+    <postGoal name="site">
+        <attainGoal name="server:copy-images"/>
+    </postGoal>
+
+    <goal name="server:copy-images">
+        <copy toDir="target/docs/images">
+            <fileSet dir="${basedir}/src/images">
+                <include name="*.gif"/>
+            </fileSet>
+        </copy>
+    </goal>
+
+    <postGoal name="antlr:generate">
+        <copy
+		file="target/antlr/org/apache/eve/schema/antlrSchemaTokenTypes.txt"
+		toDir="target/classes/org/apache/eve/schema"
+                />
+    </postGoal>
+
+</project>

Added: incubator/directory/eve/branches/start/project.properties
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/project.properties	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,16 @@
+maven.ui.banner.background=#fff
+maven.ui.source.background=#fff
+maven.ui.section.background=#6a82b6
+maven.xdoc.date=left
+maven.xdoc.version=0.7.1
+
+# Override the Modules path in build.properties
+# This defines the directory under which all of
+# the module site content will be stored.
+module.name=ldapd-server
+maven.repo.remote=http://www.ibiblio.org/maven/,http://ldapd.sourceforge.net/maven/repository/,http://cvs.apache.org/repository
+
+# antlr configuration
+maven.antlr.grammars=schema.g
+maven.antlr.src.dir=src/antlr
+

Added: incubator/directory/eve/branches/start/project.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/project.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,227 @@
+<?xml version="1.0"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+	xsi:noNamespaceSchemaLocation="maven-project.xsd">
+
+  <pomVersion>3</pomVersion>
+  <groupId>incubator-directory</groupId>
+  <id>eve-server</id>
+  <name>${module.name}</name>
+  <currentVersion>SNAPSHOT</currentVersion>
+
+  <developers>
+    <developer>
+      <name>Alex Karasulu</name>
+      <id>akarasulu</id>
+      <email>akarasulu at users.sourceforge.net</email>
+      <roles>
+        <role>PM</role>
+        <role>Founder</role>
+        <role>Developer</role>
+        <role>Architect</role>
+        <role>Lead Developer</role>
+      </roles>
+    </developer>
+
+    <developer>
+      <name>Robb Penoyer</name>
+      <id>rpenoyer</id>
+      <email>rpenoyer at users.sourceforge.net</email>
+      <roles>
+        <role>Developer</role>
+      </roles>
+    </developer>
+
+    <developer>
+      <name>Jim Bearce</name>
+      <id>bearcej</id>
+      <email>bearcej at users.sourceforge.net</email>
+      <roles>
+        <role>Project Manager</role>
+        <role>Developer</role>
+      </roles>
+    </developer>
+
+    <developer>
+      <name>Jeff Machols</name>
+      <id>jmachols</id>
+      <email>jmachols at users.sourceforget.net</email>
+      <roles>
+        <role>Developer</role>
+      </roles>
+    </developer>
+
+    <developer>
+      <name>Wes McKean</name>
+      <id>wesmckean</id>
+      <email>wesmckean at users.sourceforget.net</email>
+      <roles>
+        <role>Developer</role>
+      </roles>
+    </developer>
+
+    <developer>
+      <name>Peter Donald</name>
+      <id>donaldp</id>
+      <email>donaldp at users.sourceforge.net</email>
+      <roles>
+        <role>Advisor</role>
+        <role>Mentor</role>
+        <role>Consultant</role>
+      </roles>
+    </developer>
+
+    <developer>
+      <name>Noel Bergman</name>
+      <id>noeljb</id>
+      <email>noeljb at users.sourceforge.net</email>
+      <roles>
+        <role>Advisor</role>
+        <role>Mentor</role>
+        <role>Consultant</role>
+      </roles>
+    </developer>
+  </developers>
+    
+  <dependencies>
+	  <!-- Do we need all these dependencies?
+		log4j-core.jar
+		xdoclet-20020825.jar
+		xjavadoc-20020825.jar
+	  -->
+
+    <dependency>
+      <groupId>logkit</groupId>
+      <artifactId>logkit</artifactId>
+      <version>1.2</version>
+    </dependency>
+
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+      <version>1.0.2</version>
+      <url>http://jakarta.apache.org/commons/logging.html</url>
+    </dependency>
+
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+      <version>2.0</version>
+      <url>http://jakarta.apache.org/commons/lang</url>
+    </dependency>
+
+    <dependency>
+      <groupId>oro</groupId>
+      <artifactId>oro</artifactId>
+      <version>2.0.7</version>
+      <url>http://jakarta.apache.org/oro/index.html</url>
+    </dependency>
+
+    <dependency>
+      <groupId>jdbm</groupId>
+      <artifactId>jdbm</artifactId>
+      <version>0.12</version>
+    </dependency>
+
+    <dependency>
+      <groupId>cornerstone-threads</groupId>
+      <artifactId>cornerstone-threads-impl</artifactId>
+      <version>2.0</version>
+      <url>http://avalon.apache.org/cornerstone/</url>
+    </dependency>
+
+    <dependency>
+      <groupId>cornerstone-threads</groupId>
+      <artifactId>cornerstone-threads-api</artifactId>
+      <version>2.0</version>
+      <url>http://avalon.apache.org/cornerstone/</url>
+    </dependency>
+
+    <dependency>
+      <groupId>excalibur-thread</groupId>
+      <artifactId>excalibur-thread</artifactId>
+      <version>1.1.1</version>
+      <url>http://avalon.apache.org/excalibur/thread/index.html</url>
+    </dependency>
+
+    <dependency>
+      <groupId>avalon-phoenix</groupId>
+      <artifactId>avalon-phoenix-client</artifactId>
+      <version>4.0</version>
+      <url>http://avalon.apache.org/phoenix/</url>
+    </dependency>
+
+		<dependency>
+			<groupId>incubator-directory</groupId>
+			<artifactId>ldap-common</artifactId>
+			<version>SNAPSHOT</version>
+		</dependency>
+
+		<dependency>
+			<groupId>incubator-directory</groupId>
+			<artifactId>ldap-snacc-provider</artifactId>
+			<version>SNAPSHOT</version>
+		</dependency>
+
+		<dependency>
+			<groupId>commons-collections</groupId>
+			<artifactId>commons-collections</artifactId>
+			<version>2.1</version>
+			<url>http://jakarta.apache.org/commons/collections.html</url>
+		</dependency>
+
+		<dependency>
+			<groupId>regexp</groupId>
+			<artifactId>regexp</artifactId>
+			<version>1.2</version>
+			<url>http://jakarta.apache.org/regexp/index.html</url>
+		</dependency>
+
+		<dependency>
+			<groupId>antlr</groupId>
+			<artifactId>antlr</artifactId>
+			<version>2.7.2</version>
+			<url>http://antlr.org/</url>
+		</dependency>
+
+		<dependency>
+			<groupId>avalon-framework</groupId>
+			<artifactId>avalon-framework-api</artifactId>
+			<version>SNAPSHOT</version>
+			<url>http://avalon.apache.org/framework/</url>
+		</dependency>
+
+		<dependency>
+			<groupId>avalon-framework</groupId>
+			<artifactId>avalon-framework-impl</artifactId>
+			<version>SNAPSHOT</version>
+			<url>http://avalon.apache.org/framework/</url>
+		</dependency>
+
+	</dependencies>
+
+
+  <build>
+    <nagEmailAddress>directory-dev@incubator.apache.org</nagEmailAddress>
+    <sourceDirectory>${basedir}/src/java</sourceDirectory>
+    <sourceModifications/>
+    <unitTestSourceDirectory/>
+    <integrationUnitTestSourceDirectory/>
+    <aspectSourceDirectory/>
+    <unitTest/>
+  </build>
+    
+  <reports>
+    <report>maven-changelog-plugin</report>
+    <report>maven-developer-activity-plugin</report>
+    <report>maven-file-activity-plugin</report>
+    <report>maven-javadoc-plugin</report>
+		<report>maven-changes-plugin</report>
+		<report>maven-checkstyle-plugin</report>
+		<report>maven-file-activity-plugin</report>
+		<report>maven-javadoc-plugin</report>
+		<report>maven-jdepend-plugin</report>
+		<report>maven-jxr-plugin</report>
+		<report>maven-license-plugin</report>
+		<report>maven-tasklist-plugin</report>
+  </reports>
+</project>

Added: incubator/directory/eve/branches/start/sample.ant.properties
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/sample.ant.properties	Thu Jun 24 00:06:35 2004
@@ -0,0 +1 @@
+maven.repo=C:/Documents and Settings/Administrator/.maven/repository

Added: incubator/directory/eve/branches/start/sample.build.properties
==============================================================================

Added: incubator/directory/eve/branches/start/src/antlr/schema.g
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/antlr/schema.g	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,309 @@
+header {
+    package org.apache.eve.schema;
+    import java.util.* ;
+}
+
+// ===================================================
+//   TOKENS FOR LDAP SCHEMA SYNTAX LEXER DEFINITIONS
+//
+// -- (c) Apache Software Foundation                                     --
+// -- Please refer to the LICENSE.txt file in the root directory of      --
+// -- any directory project for copyright and distribution information.  --
+//
+//
+// ===================================================
+
+
+class antlrSchemaSyntaxLexer extends Lexer ;
+
+options {
+    k = 4 ;
+    exportVocab=antlrSchema ;
+    charVocabulary = '\3'..'\377' ;
+    caseSensitive = false ;
+    testLiterals = false ;
+}
+
+
+//COMMENT       : '#' (~'\n')* '\n'   
+//  ;
+
+WS  :   (   '#' (~'\n')* '\n' { newline(); }
+        |   ' '
+        |   '\t'
+        |   '\r' '\n' { newline(); }
+        |   '\n'      { newline(); }
+        |   '\r'      { newline(); }
+        )
+        {$setType(Token.SKIP);} //ignore this token
+    ;
+
+OPEN_PAREN      : '(' 
+    ;
+
+CLOSE_PAREN     : ')' 
+    ;
+
+OPEN_BRACKET    : '{' 
+    ;
+
+CLOSE_BRACKET   : '}' 
+    ;
+
+QUOTED_STRING   : '\'' ( ~'\'' )* '\'' 
+    ;
+
+AND             : '$'
+    ;
+
+OID             : 
+        ( '0'..'9' )+ ( '.' ( '0'..'9' )+ )* 
+            ( OPEN_BRACKET ('0' .. '9')+ CLOSE_BRACKET )?
+    ;
+
+IDENTIFIER options { testLiterals=true; }
+    : 
+        ( 'a' .. 'z') ( 'a' .. 'z' | '0' .. '9' | '-')*
+    ;
+
+
+class antlrSchemaParser extends Parser ;
+
+
+tokens {
+    ATTRIBUTE_TYPE          =   "attributetype"         ;
+    NAME_KW                 =   "NAME"                  ;
+    EQUALITY_KW             =   "EQUALITY"              ;
+    ORDERING_KW             =   "ORDERING"              ;
+    SYNTAX_KW               =   "SYNTAX"                ;
+    SINGLEVAL_KW            =   "SINGLE-VALUE"          ;
+    NOUSERMOD_KW            =   "NO-USER-MODIFICATION"  ;
+    USAGE_KW                =   "USAGE"                 ;
+    DESC_KW                 =   "DESC"                  ;
+    MAY_KW                  =   "MAY"                   ;
+    MUST_KW                 =   "MUST"                  ;
+    SUBSTR_KW               =   "SUBSTR"                ;
+    SUP_KW                  =   "SUP"                   ;
+    AUXILIARY_KW            =   "AUXILIARY"             ;
+    STRUCTURAL_KW           =   "STRUCTURAL"            ;
+    ABSTRACT_KW             =   "ABSTRACT"              ;
+}
+
+
+schemafile [SchemaImpl a_schema]:
+{
+    AttributeSpec l_attribute = null ;
+    ObjectClassSpec l_objectClass = null ;
+}
+    ( l_attribute=attributedef 
+        {
+            a_schema.addAttributeSpec(l_attribute) ;
+        }
+    | 
+    l_objectClass=objectclassdef 
+        {
+            a_schema.addObjectClassSpec(l_objectClass) ;
+        }
+    )+ EOF ; 
+
+
+attributedef returns [AttributeSpec l_attribute]
+{
+    l_attribute = new AttributeSpec() ;
+    ArrayList l_nameList = null ;
+}
+    :
+    ATTRIBUTE_TYPE OPEN_PAREN oid:OID l_nameList=namelist 
+        {
+            l_attribute.m_oid = oid.getText() ;
+            l_attribute.m_nameList = l_nameList ;
+        }
+        (   
+        ( "DESC" desc:QUOTED_STRING 
+            {
+                String quoted = desc.getText() ;
+                l_attribute.m_desc = quoted.substring(1, quoted.length() - 1) ;
+            }
+        ) |
+        
+
+        ( "EQUALITY" equality:IDENTIFIER 
+            {
+                l_attribute.m_equality = equality.getText().toLowerCase() ;
+            }
+        ) |
+
+
+        ( "SUBSTR" substr:IDENTIFIER
+            {
+                l_attribute.m_substr = substr.getText().toLowerCase() ;
+            }
+        ) |
+
+
+        ( "ORDERING" ordering:IDENTIFIER
+            {
+                l_attribute.m_ordering = ordering.getText().toLowerCase() ;
+            }
+        ) |
+
+
+        ( "SYNTAX" syntax:OID
+            {
+                l_attribute.m_syntax = syntax.getText() ;
+            }
+        ) |
+
+
+        ( "SINGLE-VALUE" 
+            {
+                l_attribute.m_isSingleValue = true ;
+            }
+        ) |
+
+        ( "NO-USER-MODIFICATION"
+            {
+                l_attribute.m_canUserModify = false ;
+            }
+        ) |
+
+        ( "SUP" sup:IDENTIFIER 
+            {
+                l_attribute.m_superClass = sup.getText().toLowerCase() ;
+            }
+        ) |
+
+
+        ( "USAGE" usage:IDENTIFIER
+            {
+                l_attribute.m_usage = usage.getText().toLowerCase() ;
+            }
+        )           
+        )* CLOSE_PAREN ;
+
+
+objectclassdef returns [ObjectClassSpec l_objectClass]
+{
+    l_objectClass = new ObjectClassSpec() ;
+    ArrayList l_mayList = null ;
+    ArrayList l_mustList = null ;
+    ArrayList l_superClasses = null ;
+    ArrayList l_nameList = null ;
+    
+}
+    :
+    "objectclass" OPEN_PAREN oid:OID l_nameList=namelist
+        {
+            l_objectClass.oid = oid.getText() ;
+            l_objectClass.nameList = l_nameList ;
+        }
+        (
+        ( "DESC" desc:QUOTED_STRING 
+            {
+                String tmp = desc.getText() ;
+                l_objectClass.desc = tmp.substring(1, tmp.length() - 1) ;
+            }
+        ) |
+        ( l_superClasses=superclasslist 
+            {
+                l_objectClass.superClasses = l_superClasses ;
+            }
+        ) |
+        ( "ABSTRACT" 
+            { l_objectClass.type = ObjectClassSpec.ABSTRACT   ; }
+            | "STRUCTURAL" 
+            { l_objectClass.type = ObjectClassSpec.STRUCTURAL ; }
+            | "AUXILIARY" 
+            { l_objectClass.type = ObjectClassSpec.AUXILIARY  ; }
+
+        ) |
+        ( l_mustList=mustlist 
+            {
+                l_objectClass.mustList = l_mustList ;
+            }
+        ) |
+        ( l_mayList=maylist 
+            {
+                l_objectClass.mayList = l_mayList ;
+            }
+        ) 
+        )* CLOSE_PAREN ;
+
+        
+superclasslist returns [ArrayList l_supList]
+{
+    l_supList = new ArrayList() ; 
+}
+    :
+    "SUP"  ( id:IDENTIFIER 
+        {
+            l_supList.add(id.getText().toLowerCase()) ;
+        }
+
+            | OPEN_PAREN id2:IDENTIFIER 
+        {
+            l_supList.add(id2.getText().toLowerCase()) ;
+        }
+        
+            ( AND id3:IDENTIFIER 
+
+        {
+            l_supList.add(id3.getText().toLowerCase()) ;
+        }
+        
+        )* CLOSE_PAREN ) ;
+
+
+namelist returns [ArrayList l_nameList]
+{ 
+    l_nameList = new ArrayList() ; 
+}
+    : 
+    "NAME" ( name:QUOTED_STRING 
+        {
+            String tmp = name.getText() ;
+            tmp = tmp.substring(1, tmp.length() - 1) ;
+            l_nameList.add(tmp) ;
+        }
+
+        | OPEN_PAREN (name2:QUOTED_STRING
+
+        {
+            String tmp = name2.getText() ;
+            tmp = tmp.substring(1, tmp.length() - 1) ;
+            l_nameList.add(tmp) ;
+        }
+
+            )+ CLOSE_PAREN ) ;
+
+
+
+maylist returns [ArrayList l_mayList]
+{ 
+    l_mayList = null ; 
+}
+    : "MAY" ( IDENTIFIER | l_mayList=attributelist ) ;
+
+
+
+mustlist returns [ArrayList l_mustList]
+{ 
+    l_mustList = null ; 
+}
+    : 
+    "MUST" ( IDENTIFIER | l_mustList=attributelist ) ;
+
+
+attributelist returns [ArrayList l_list]
+{ 
+    l_list = new ArrayList() ; 
+}
+    :
+    OPEN_PAREN id:IDENTIFIER 
+        { l_list.add(id.getText().toLowerCase()) ; }
+        ( AND id2:IDENTIFIER { l_list.add(id2.getText().toLowerCase()) ; })* 
+    CLOSE_PAREN ;
+
+
+
+

Added: incubator/directory/eve/branches/start/src/conf/assembly.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/conf/assembly.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,239 @@
+<?xml version="1.0"?>
+<!DOCTYPE assembly PUBLIC "-//PHOENIX/Assembly DTD Version 1.0//EN" 
+    "http://jakarta.apache.org/avalon/dtds/phoenix/assembly_1_0.dtd">
+<assembly>
+
+	<!-- ======================================================================
+
+
+
+					  C O R N E R S T O N E   B L O C K S
+
+
+
+    ======================================================================= -->
+
+
+	<!-- ======================================================================
+			  		T H R E A D   M A N A G E R   B L O C K
+    ======================================================================= -->
+
+	<block
+    		class="org.apache.avalon.cornerstone.blocks.threads.DefaultThreadManager"
+    	   	name="thread-manager"/>
+
+
+
+	<!-- ======================================================================
+
+
+
+					  S Y S T E M   M O D U L E   B L O C K S
+
+
+
+    ======================================================================= -->
+
+
+	<!-- ======================================================================
+					  O U T P U T   M O D U L E   B L O C K
+    ======================================================================= -->
+
+	<block
+		class="org.apache.eve.output.OutputModule"
+		name="output">
+	
+		<provide
+			name="thread-manager"
+			role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
+	</block>
+
+	<!-- ======================================================================
+					   I N P U T   M O D U L E   B L O C K
+    ======================================================================= -->
+
+	<block
+		class="org.apache.eve.input.InputModule"
+		name="input">
+
+		<provide
+			name="decoder"
+			role="org.apache.eve.decoder.Decoder" />
+		<provide
+			name="thread-manager"
+			role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
+	</block>
+
+	<!-- ======================================================================
+					  S C H E M A   M O D U L E   B L O C K
+    ======================================================================= -->
+
+	<block
+		class="org.apache.eve.schema.SchemaModule"
+		name="schema-manager"/>
+
+	<!-- ======================================================================
+				B A C K E N D   N E X U S   M O D U L E   B L O C K
+    ======================================================================= -->
+
+	<block
+		class="org.apache.eve.backend.NexusModule"
+		name="nexus">
+		<provide
+			name="schema-manager"
+			role="org.apache.eve.schema.SchemaManager" />
+	</block>
+
+	<!-- ======================================================================
+					L I S T E N E R   M O D U L E   B L O C K
+    ======================================================================= -->
+
+	<block
+		class="org.apache.eve.listener.ListenerModule"
+		name="listener">
+		<provide
+			name="client"
+			role="org.apache.eve.client.ClientManager" />
+	</block>
+
+	<!-- ======================================================================
+					  C L I E N T   M O D U L E   B L O C K
+    ======================================================================= -->
+
+	<block
+		class="org.apache.eve.client.ClientModule"
+		name="client">
+		<provide
+			name="decoder"
+			role="org.apache.eve.decoder.Decoder" />
+		<provide
+			name="input"
+			role="org.apache.eve.input.InputManager" />
+		<provide
+			name="output"
+			role="org.apache.eve.output.OutputManager" />
+		<provide
+			name="nexus"
+			role="org.apache.eve.backend.UnifiedBackend" />
+		<provide
+			name="protocol"
+			role="org.apache.eve.protocol.ProtocolEngine" />
+		<provide
+			name="thread-manager"
+			role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
+	</block>
+
+	<!-- ======================================================================
+					  E N C O D E R   M O D U L E   B L O C K
+    ======================================================================= -->
+
+	<block
+		class="org.apache.eve.encoder.EncoderModule"
+		name="encoder">
+
+		<provide
+			name="output"
+			role="org.apache.eve.output.OutputManager" />
+		<provide
+			name="thread-manager"
+			role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
+	</block>
+
+	<!-- ======================================================================
+					  D E C O D E R   M O D U L E   B L O C K
+    ======================================================================= -->
+
+	<block
+		class="org.apache.eve.decoder.DecoderModule"
+		name="decoder">
+		<provide
+			name="protocol"
+			role="org.apache.eve.protocol.ProtocolEngine" />
+		<provide
+			name="thread-manager"
+			role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
+	</block>
+
+	<!-- ======================================================================
+					 P R O T O C O L   M O D U L E   B L O C K
+    ======================================================================= -->
+
+	<block
+		class="org.apache.eve.protocol.ProtocolModule"
+		name="protocol">
+
+		<provide
+			name="event"
+			role="org.apache.eve.event.protocol.EventManager" />
+
+		<provide
+			name="nexus"
+			role="org.apache.eve.backend.UnifiedBackend" />
+
+		<provide
+			name="authman"
+			role="org.apache.eve.security.auth.AuthenticationManager" />
+
+		<provide
+			name="encoder"
+			role="org.apache.eve.encoder.Encoder" />
+
+		<provide
+			name="output"
+			role="org.apache.eve.output.OutputManager" />
+
+		<provide
+			name="thread-manager"
+			role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
+	</block>
+
+	<!-- ======================================================================
+			    J D B M   B A C K E N D   M O D U L E   B L O C K
+    ======================================================================= -->
+
+	<block
+		class="org.apache.eve.backend.jdbm.JdbmModule"
+		name="backend0">
+		<provide
+			name="schema-manager"
+			role="org.apache.eve.schema.SchemaManager" />
+		<provide
+			name="nexus"
+			role="org.apache.eve.backend.UnifiedBackend" />
+	</block>
+
+	<!-- ======================================================================
+				A U T H E N T I C A T I O N   M O D U L E   B L O C K
+    ======================================================================= -->
+
+	<block
+		class="org.apache.eve.security.auth.AuthenticationModule"
+		name="authman">
+		<provide
+			name="nexus"
+			role="org.apache.eve.backend.UnifiedBackend" />
+	</block>
+
+
+	<!-- ======================================================================
+				J N D I   P R O V I D E R   M O D U L E   B L O C K
+    ======================================================================= -->
+
+	<block
+		class="org.apache.eve.jndi.JndiProviderModule"
+		name="jndi">
+		<provide
+			name="nexus"
+			role="org.apache.eve.backend.UnifiedBackend" />
+	</block>
+
+	<!-- ======================================================================
+				E V E N T   M A N A G E R   M O D U L E   B L O C K
+    ======================================================================= -->
+
+	<block
+		class="org.apache.eve.event.protocol.EventModule"
+		name="event">
+	</block>
+
+</assembly>

Added: incubator/directory/eve/branches/start/src/conf/config.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/conf/config.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,376 @@
+<config>
+
+	<!-- ======================================================================
+    			T H R E A D   M A N A G E R   C O N F I G
+    ======================================================================= -->
+
+	<thread-manager>
+
+		<!-- Used by OutputManager for writing to client streams -->
+
+		<thread-group>
+			<name>output</name>
+			<priority>3</priority>
+			<is-daemon>false</is-daemon>
+			<max-threads>5</max-threads>
+			<min-threads>1</min-threads>
+			<min-spare-threads>3</min-spare-threads>
+		</thread-group>
+
+
+
+		<!-- Used by InputManager for monitoring client connections -->
+
+		<thread-group>
+			<name>client</name>
+			<priority>3</priority>
+			<is-daemon>false</is-daemon>
+			<max-threads>5</max-threads>
+			<min-threads>1</min-threads>
+			<min-spare-threads>3</min-spare-threads>
+		</thread-group>
+
+
+
+		<!-- Used by ClientManager Stage -->
+
+		<thread-group>
+			<name>clientstage</name>
+			<priority>3</priority>
+			<is-daemon>false</is-daemon>
+			<max-threads>5</max-threads>
+			<min-threads>1</min-threads>
+			<min-spare-threads>3</min-spare-threads>
+		</thread-group>
+
+
+
+		<!-- Used for the ProtocolEngine processors and should be at least 
+			equal to the 'client' pool if not more -->
+
+		<thread-group>
+			<name>processor</name>
+			<priority>3</priority>
+			<is-daemon>false</is-daemon>
+			<max-threads>5</max-threads>
+			<min-threads>1</min-threads>
+			<min-spare-threads>3</min-spare-threads>
+		</thread-group>
+
+
+
+		<!-- Used for the Encoder and should be at least 
+			equal to the 'client' pool if not more -->
+
+		<thread-group>
+			<name>encoder</name>
+			<priority>3</priority>
+			<is-daemon>false</is-daemon>
+			<max-threads>5</max-threads>
+			<min-threads>1</min-threads>
+			<min-spare-threads>3</min-spare-threads>
+		</thread-group>
+
+
+
+		<!-- Used for the Decoder and should be at least 
+			equal to the 'client' pool if not more -->
+
+		<thread-group>
+			<name>decoder</name>
+			<priority>3</priority>
+			<is-daemon>false</is-daemon>
+			<max-threads>5</max-threads>
+			<min-threads>1</min-threads>
+			<min-spare-threads>3</min-spare-threads>
+		</thread-group>
+
+
+
+		<!-- Everything else -->
+		<thread-group>
+			<name>default</name>
+			<priority>2</priority>
+			<is-daemon>false</is-daemon>
+			<max-threads>4</max-threads>
+			<min-threads>1</min-threads>
+			<min-spare-threads>3</min-spare-threads>
+		</thread-group>
+	</thread-manager>
+
+
+	<!-- ======================================================================
+							S C H E M A   C O N F I G
+    ======================================================================= -->
+
+	<schema-manager>
+    	<!-- S T A G E : I  -->
+		<!-- files are parsed first to build file schemas -->
+
+    	<schema name="our"
+        	filepath="schema/our.schema" />
+    	<schema name="core"
+        	filepath="schema/core.schema" />
+    	<schema name="vendor"
+        	filepath="schema/vendor.schema" />
+    	<schema name="corba"
+        	filepath="schema/corba.schema" />
+    	<schema name="cosine"
+        	filepath="schema/cosine.schema" />
+    	<schema name="java"
+        	filepath="schema/java.schema" />
+    	<schema name="misc"
+        	filepath="schema/misc.schema" />
+
+		<!--
+    	<schema name="nis"
+        	filepath="schema/nis.schema" />
+    	<schema name="krb5-kdc"
+        	filepath="schema/krb5-kdc.schema" />
+		-->
+    	<schema name="inetorgperson"
+        	filepath="schema/inetorgperson.schema" />
+
+    	<!-- S T A G E : II  -->
+		<!-- built-ins: deepTrimToLower, deepTrim, trim, dnNormalize, asis -->
+        <!-- regex version is perl5 -->
+
+		<normalization default="asis">
+        	<normalizer op="deepTrimToLower" rule="caseIgnoreMatch"/>
+        	<normalizer op="deepTrimToLower" rule="caseIgnoreListMatch"/>
+        	<normalizer op="deepTrimToLower" rule="caseIgnoreIA5Match"/>
+			<normalizer op="deepTrimToLower" rule="objectidentifiermatch"/>
+        	<normalizer op="deepTrim" rule="caseExactIA5Match"/>
+        	<normalizer op="deepTrim" rule="telephoneNumberMatch"/>
+        	<normalizer op="dnNormalize" rule="distinguishedNameMatch"/>
+
+            <normalizer rule="someNewBogusRule">
+            	<regex value="s/abc/123/g"/>
+            	<regex value="/ /d"/>
+            	<regex value="y/def/DEF/"/>
+            </normalizer>
+        </normalization>
+
+    	<!-- S T A G E : III  -->
+		<!-- built-ins: accept, . . ., TBA -->
+        <!-- regex version is perl5 -->
+		<syntax-checkers default="accept">
+        	<!-- fictitious: matches yes, Yes, YES etc -->
+			<syntax-checker oid="1.2.34.2.1.2.3.4523.1.56.4.2.34.5345.3">
+            	<regex value="/yes/i"/>
+            </syntax-checker>
+        </syntax-checkers>
+
+        <!-- S T A G E : IV  -->
+        <!-- Subschema Administrative Area Used by backend0 -->
+	    <SAA dn="dc=example,dc=com">
+	        <schema-ref schema="our"/>
+	        <schema-ref schema="core"/>
+	        <schema-ref schema="cosine"/>
+	        <schema-ref schema="corba"/>
+	        <schema-ref schema="java"/>
+	        <schema-ref schema="misc"/>
+	        <schema-ref schema="inetorgperson"/>
+        </SAA>
+    </schema-manager>
+
+
+	<!-- ======================================================================
+			A U T H E N T I C A T I O N   M A N A G E R   C O N F I G
+    ======================================================================= -->
+
+	<authman>
+	</authman>
+
+
+	<!-- ======================================================================
+				U N I F I E D   B A C K E N D   N E X U S   C O N F I G
+    ======================================================================= -->
+
+	<nexus>
+		<RootDSE>
+			<attribute name="namingContexts" value="dc=example,dc=com" />
+
+			<!-- Currently in this config there is no replica 
+			The examples below are of alternative server's that contain
+			the same naming contexts that this server contains.
+			
+			<attribute name="altServer" value="ldap://replica1:389" />
+			<attribute name="altServer" value="ldap://replica2:389" />
+			-->
+
+			<!-- Currently there are no supported extended ops
+			The extention below is RefreshRequest protocol operation defined as an
+			extention for dynamic directories see http://www.ietf.org/rfc/rfc2589.txt
+			for more information.
+
+			<attribute name="supportedExtension" value="1.3.6.1.4.1.1466.101.119.1" />
+			-->
+
+			<!-- Currently there are no supported control ops
+			These below are VLV (Virtual List View) controls and the
+			LDAP Control for a Duplicate Entry Representation of Search Results
+
+			<attribute name="supportedControl" value="2.16.840.1.113730.3.4.9" />
+			<attribute name="supportedControl" value="2.16.840.1.113730.3.4.10" />
+			<attribute name="supportedControl" value="2.16.840.1.113719.1.27.101.1" />
+			<attribute name="supportedControl" value="2.16.840.1.113719.1.27.101.2" />
+			<attribute name="supportedControl" value="2.16.840.1.113719.1.27.101.3" />
+			-->
+
+			<!-- Currently SASL is not implemented yet.
+			The following URL explains more about the SASL mechanisms used:
+			http://www.ietf.org/rfc/rfc2829.txt
+
+			<attribute name="supportedSASLMechanisms" 
+				value="ANONYMOUS PLAIN OTP DIGEST-MD5 CRAM-MD5" />
+			-->
+
+			<attribute name="subschemaSubentry" value="cn=subschema" />
+			<attribute name="supportedLDAPVersion" value="3" />
+			<attribute name="vendorName" value="ASF" />
+			<attribute name="vendorVersion" value="eve-0.01" />
+		</RootDSE>
+	</nexus>
+
+
+	<!-- ======================================================================
+						I N P U T   M O D U L E   C O N F I G
+    ======================================================================= -->
+
+	<output>
+        <stage name="output" poolname="output">
+		</stage>
+	</output>
+
+
+	<!-- ======================================================================
+						I N P U T   M O D U L E   C O N F I G
+    ======================================================================= -->
+
+	<input>
+	</input>
+
+
+	<!-- ======================================================================
+						    D E C O D E R   C O N F I G
+    ======================================================================= -->
+
+	<decoder>
+        <stage name="decoder" poolname="decoder">
+		</stage>
+	</decoder>
+
+
+	<!-- ======================================================================
+				     		E N C O D E R   C O N F I G
+    ======================================================================= -->
+
+	<encoder>
+        <stage name="encoder" poolname="encoder">
+		</stage>
+	</encoder>
+
+
+	<!-- ======================================================================
+				     P R O T O C O L   E N G I N E   C O N F I G
+    ======================================================================= -->
+
+	<protocol>
+        <stage name="protocol" poolname="processor">
+		</stage>
+	</protocol>
+
+
+	<!-- ======================================================================
+					S E R V E R   L I S T E N E R   C O N F I G
+    ======================================================================= -->
+
+		
+ 	<listener>
+		<port>1396</port>
+        <host>localhost</host>
+        <backlog>50</backlog>
+	</listener>
+
+	<!--
+ 	<listener>
+		<port>1396</port>
+        <host>172.18.9.200</host>
+        <backlog>50</backlog>
+	</listener>
+	-->
+
+
+	<!-- ======================================================================
+				C L I E N T   M A N A G E R   S T A G E   C O N F I G
+    ======================================================================= -->
+
+	<client>
+        <stage name="client" poolname="clientstage">
+		</stage>
+	</client>
+
+
+	<!-- ======================================================================
+				J D B M   B A C K E N D   ( 0 )   C O N F I G
+    ======================================================================= -->
+
+	<backend0>
+		<!-- Backend configuration properties -->
+
+
+		<!-- ==================================================================
+		Suffix Entry Definition:
+				
+			 The suffix entry is automatically created for the first time
+			 when the backend is brought up and it is not found.  If it 
+			 already exists then this section is used simply to get the
+			 Dn to the backend.
+
+		=================================================================== -->
+
+		<suffix>
+			<attribute name="distinguishedname" value="dc=example,dc=com" />
+			<attribute name="objectclass" value="top" />
+			<attribute name="objectclass" value="domain" />
+			<attribute name="dc" value="example" />
+		</suffix>
+
+		<adminUserDN>cn=admin,dc=example,dc=com</adminUserDN>
+		<adminUserPassword>jPasswordField1</adminUserPassword>
+		<workingDirPath>var/backend0</workingDirPath>
+		<entryCacheSize>1000</entryCacheSize>
+
+		<!-- Specific To Jdbm DB Backend -->
+		<indices>
+			<index name="l"/>
+			<index name="ou"/>
+			<index name="cn"/>
+			<index name="sn"/>
+			<index name="dc"/>
+			<index name="uid"/>
+			<index name="mail"/>
+			<index name="givenName"/>
+			<index name="roomnumber"/>
+			<index name="objectclass"/>
+		</indices>
+	</backend0>
+
+
+	<!-- ======================================================================
+				J N D I   P R O V I D E R   M O D U L E   C O N F I G
+    ======================================================================= -->
+
+	<jndi>
+	</jndi>
+
+
+	<!-- ======================================================================
+					E V E N T   M O D U L E   C O N F I G
+    ======================================================================= -->
+
+	<event>
+	</event>
+
+</config>

Added: incubator/directory/eve/branches/start/src/conf/environment.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/conf/environment.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,132 @@
+<?xml version="1.0"?>
+<!--
+                                README!
+
+    Basic config file that sets up context for server application.
+
+
+-->
+
+<environment>
+	<logs>
+		<!--
+
+							L O G   C A T E G O R I E S
+							===========================
+
+        Note that a log <category> element's name attribute corresponds to the
+        name of the block in the assembly.xml file. Hence each block is a log
+        category in this configuration.
+
+        Also the target attribute of the <category> element is the name of the
+        <log-target> element's name attribute which determines where or rather
+        which log file we route the log event to.
+
+        -->
+
+		<category name=""
+        		  target="default"
+                  priority="DEBUG"/>
+
+		<category name="jndi"
+        		  target="jndi"
+                  priority="DEBUG"/>
+
+		<category
+        		  name="input"
+                  target="io"
+                  priority="DEBUG"/>
+
+		<category
+        		  name="output"
+                  target="io"
+                  priority="DEBUG"/>
+
+		<category
+        		  name="nexus"
+                  target="backend-modules"
+                  priority="DEBUG"/>
+
+		<category
+        		  name="backend0"
+                  target="backend-modules"
+                  priority="DEBUG"/>
+
+		<category
+        		  name="authman"
+                  target="authentication"
+                  priority="DEBUG"/>
+
+		<category
+        		  name="protocol"
+                  target="protocol"
+                  priority="DEBUG"/>
+
+		<category
+        		  name="listener"
+                  target="listener"
+                  priority="DEBUG"/>
+
+		<category
+        		  name="client"
+                  target="listener"
+                  priority="DEBUG"/>
+
+		<category
+        		  name="schema-manager"
+                  target="schema"
+                  priority="DEBUG"/>
+
+		<category
+        		  name="encoder"
+                  target="asn1"
+                  priority="DEBUG"/>
+
+		<category
+        		  name="decoder"
+                  target="asn1"
+                  priority="DEBUG"/>
+
+
+		<category name="event"
+        		  target="event"
+                  priority="DEBUG"/>
+
+		<!-- Logfile for Event Manager -->
+		<log-target name="event" location="/logs/event.log"/>
+
+		<!-- Logfile for Input/Output Managers -->
+		<log-target name="io" location="/logs/io.log"/>
+
+		<!-- Logfile for ASN1 BER Encoding/Decoding -->
+		<log-target name="asn1" location="/logs/asn1.log"/>
+
+		<!-- Logfile for Server Side Jndi Provider -->
+		<log-target name="jndi" location="/logs/jndi.log"/>
+
+		<!-- Logfile for server listener -->
+		<log-target name="listener" location="/logs/listener.log"/>
+
+		<!-- Logfile for backends and the backend manager -->
+		<log-target name="backend-modules" location="/logs/backends.log"/>
+
+		<!-- Logfile for the authentication module -->
+		<log-target name="authentication" location="/logs/authentication.log"/>
+
+		<!-- Logfile for the protocol server module -->
+		<log-target name="protocol" location="/logs/protocol.log"/>
+
+        <!-- Default where everything else goes -->
+		<log-target name="default" location="/logs/misc.log"/>
+
+        <!-- Schema Manager Log File -->
+		<log-target name="schema" location="/logs/schema.log"/>
+	</logs>
+
+	<policy>
+		<grant code-base="sar:SAR-INF/lib/*">
+			<permission class="java.security.AllPermission"/>
+		</grant>
+	</policy>
+
+</environment>

Added: incubator/directory/eve/branches/start/src/docbook/design/authentication-module.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/authentication-module.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.3 $</revnumber>
+                <date>$Date: 2003/05/03 01:21:32 $</date>
+                <revdescription>
+                    <para>
+$Log: authentication-module.xml,v $
+Revision 1.3  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.1  2003/03/10 23:24:19  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.1  2003/03/03 04:49:25  akarasulu
+Added as placeholders for now.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+        </para>
+
+        <para>
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/backend-module.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/backend-module.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,490 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.6 $</revnumber>
+                <date>$Date: 2003/05/03 01:43:08 $</date>
+                <revdescription>
+                    <para>
+$Log: backend-module.xml,v $
+Revision 1.6  2003/05/03 01:43:08  bearcej
+Add maven file to help generate server site.
+Modify graphic links again.
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.5  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.4  2003/04/21 21:11:50  bearcej
+
+* Fix broken links in documentation.
+
+Revision 1.3  2003/04/08 02:29:55  bearcej
+* Clean up project.properties
+* Fix logos in project.xml
+* Fix links to image files in docs
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.5  2003/03/15 17:35:24  bearcej
+Fix href to BackendInterface.gif
+
+Revision 1.1.2.4  2003/03/15 17:07:26  bearcej
+Fix figure tags to conform to simple docbook (note: maven will not handle
+correctly without a maven patch)
+Modified Files:
+ Tag: ALPHA-0_7
+    backend-module.xml
+
+Revision 1.1.2.3  2003/03/12 02:11:56  akarasulu
+Checking partial backend module docs.
+
+Revision 1.1.2.2  2003/03/11 01:26:41  akarasulu
+Started up BackendModule documentation - more to come soon.
+
+Revision 1.1.2.1  2003/03/10 23:24:19  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.2  2003/03/10 23:12:06  akarasulu
+Moved these images to src/xdocs/design
+
+Revision 1.1.2.1  2003/03/03 04:49:25  akarasulu
+Added as placeholders for now.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+            The BackendModule is an abstract base class which implements the
+            AtomicBackend service interface.  The abstract module provides a 
+            starting point for backend module developers.  Backend modules are 
+            the Directory Information Bases (DIB) which store entries served 
+            by the directory.  Several backends of various types may coexist 
+            within the same directory under different naming contexts.  The goal
+            of this document is to describe the server to backend contract 
+            specified by interfaces within the org.apache.eve.backend package.
+            Special emphesis will be placed on the the AtomicBackend service 
+            interface, as well as other helper interfaces and classes.  
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Architecture</title>
+        
+        <para>
+            Backends relate one to one with naming contexts exposed by the
+            server.  Backends are specialized databases customized for the 
+            storage and retieval of entries participating within a DIB's 
+            namespace.  The LDAP access model and naming model are tightly 
+            intertwined.  The interfaces defining a backend module reflect
+            the tight coupling between these models.  The operations against
+            backends fit into one of three categories: configuration parameter 
+            methods, structural DIT management operations and nCRUD entry 
+            life-cycle operations.
+        </para>
+        
+        <para>
+            Backends require interfaces to Create, Read, Update and Delete 
+            (CRUD) entries.  These operations form the basis the CRUD life-cycle
+            model for in-memory objects repeatedly retrived from and stored
+            back to disk.  For the purpose of this discussion, we refer to the
+            copy held on disk as the disk shadow copy.  It shadows the in-memory
+            image rather than mirroring it, since at any instant in time the
+            in-memory image may not be the same as the shadowed image on disk.
+            Under such circumstances the in-memory image is considered dirty.
+            The process of applying the computations used to synchronize 
+            in-memory images is called store optimization.  Store optimization
+            reverts dirty entires to clean entries.  Every backing store 
+            confronts the task of store optimization at some point.  A backend 
+            module is no exception and the exact means used to acheive 
+            synchronization is highly specific to the backing store 
+            implementation.  Regardless of the backend implementation, the 
+            architecture strives to abstract away storage mechanisms to present
+            a common entry life-cycle facade.  The use of the CRUD model 
+            achives this goal by breaking down the life-cycle into small atomic
+            operations easily implemented by encapsulating specific data 
+            storage mechanisms.
+        </para>
+        
+        <para>
+            Under CRUD, the addition of an entry to a backend requires an 
+            initial set of attributes, followed by a create operation.  The
+            create operation arguments provide the initial attribute set 
+            required to prevent a schema violation based on the entry's
+            objectclasses.  The identifiers, values and number of attributes 
+            composing the initial set vary with respect to the entry.  If the 
+            language supports variable argument lists, attribute ids and values 
+            could be provided directly as create arguments.  Better yet an 
+            empty entry object without a respective shadow copy on disk, could
+            be instanciated and populated with attributes.  The in-memory
+            entry object could then be used as the sole argument to a create 
+            operation passing in all required attributes and their values in
+            one object.  The latter approach leads to cleaner code and to a 
+            slightly altered version of the CRUD model.  Instantiation of the
+            in-memory entry shell, without a shadowed disk image, adds the
+            less central 'New' operation to CRUD.  The addition of the 'New' 
+            operation yeilds the modified CRUD or nCRUD model.  The core Backend
+            super interface supports the nCRUD entry life-cycle model by 
+            declaring the following methods:
+        </para>
+        
+        <table id="tab1">
+            <title>Supported nCRUD Operations</title>
+            <tgroup cols="5">
+                <thead>
+                    <row><entry>Method</entry>
+                    <entry>CRUD Op</entry>
+                    <entry>Source State</entry>
+                    <entry>Target State</entry>
+                    <entry>Description</entry></row>
+                </thead>
+                <tbody>
+                    <row>
+                        <entry>newEntry(String a_dn):LdapEntry</entry>
+                        <entry>New</entry>
+                        <entry>NA</entry>
+                        <entry>New</entry>
+                        <entry>
+                            Creates a new invalid LdapEntry object instance
+                            only.  Invalid entries are not shadowed in the 
+                            backing store, they are merely empty containers.
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>create(LdapEntry a_entry):void</entry>
+                        <entry>Create</entry>
+                        <entry>New</entry>
+                        <entry>Clean</entry>
+                        <entry>
+                            Clean entries are valid LdapEntry object 
+                            instances that are consistant with their mirror
+                            image in the backing store.
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>read(Name a_dn):LdapEntry</entry>
+                        <entry>Read</entry>
+                        <entry>NA</entry>
+                        <entry>Clean</entry>
+                        <entry>
+                            Instantiates a valid LdapEntry instance to 
+                            mirror the copy held within the backing store.
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>update(LdapEntry):void</entry>
+                        <entry>Update</entry>
+                        <entry>Dirty</entry>
+                        <entry>Clean</entry>
+                        <entry>
+                            Updates the shadow copy in the backing store to
+                            mirror the in memory LdapEntry image supplied.
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>delete(LdapEntry):void</entry>
+                        <entry>Delete</entry>
+                        <entry>Clean|Dirty</entry>
+                        <entry>Deleted</entry>
+                        <entry>
+                            Deletes the shadowed copy within the backing 
+                            store, and invalidates the LdapEntry argument.
+                        </entry>
+                    </row>
+                </tbody>
+            </tgroup>
+        </table>
+            
+        <para>
+            The Backend super interface declares the minimum set of operations
+            required to manage a DIB.  These methods include the nCRUD 
+            operations tabulated in Table I above.  nCRUD operations model the 
+            entry life-cycle causing transitions between entry states depicted 
+            in Figure I below.  The states are implied abstract nCRUD states.  
+            They are not necessarily modeled explicity within any 
+            implementation.
+        </para>
+        
+        <figure id="fig1">
+            <title>CRUD Life-cycle States</title>
+            <graphic fileref="../images/CRUDLifecycle.gif"/>
+        </figure>
+        
+        <para>
+            The Backend interface also declares DIB namespace management 
+            operations.  Namespace management operations affect the structure
+            of the DIB without adding or removing entries.  Name modifications 
+            upon entries occur to reflect namespace alterations.  Entry 
+            attributes other than those used for the relative distinguished 
+            name (RDN) never change.  Examples of such operations are move()
+            and modifyRdn().  Other Backend interface operations like search(), 
+            getParent(), hasEntry() and listChildren(), do not fit into either 
+            the nCRUD category nor do they fit into the catagory of namespace 
+            operations.  They are modified Read operations which enable specific
+            lookups and generalized mass Reads as DIB queries.
+        </para>
+        
+        <para>
+            The Backend super interface is extended by two sub interfaces:
+            UnifiedBackend and AtomicBackend.  Both are Avalon service 
+            interfaces.  <ulink url="http://avalon.apache.org/framework/">
+            Avalon Framework</ulink> defines the concept of a service as a
+            interface fulfilling a role within a system.  The idea leads to
+            Service Oriented Programing (SOP), which breaks a system down into 
+            a set of services.  By convention Avalon, differentiates service
+            interfaces from ordinary interfaces based on the present of a ROLE
+            constant.  The ROLE constant is a String set to the fully qualified
+            name of the service interface.  An example of a service interface
+            is shown below:
+        </para>
+        
+        <programlisting>
+            package com.calculator
+            
+            interface CalculatorService 
+            {
+                String ROLE = "com.calculator.CalculatorService" ;
+                
+                int add(int a_arg1, int a_arg2) ;
+                int sub(int a_arg1, int a_arg2) ;
+                int div(int a_arg1, int a_arg2) ;
+                int mlt(int a_arg1, int a_arg2) ;
+            }
+        </programlisting>
+        
+        <para>
+            The CalculatorService service interface exposes a set of arithmetic
+            operations accessible to other services within a system.  Note the
+            ROLE constant which is set to the fully qualified name of the 
+            interface.   Unlike this example service interface the Backend 
+            interface does not have a ROLE constant, hence it is not a service 
+            interface.  The Backend interface alone cannot be implemented by a 
+            module within the server, instead one of its sub interfaces must be
+            implemented.
+        </para>
+        
+        <para>
+            The AtomicBackend interface is a service interface according to the
+            Avalon definition with a ROLE constant set to the fully qualified
+            name of AtomicBackend: org.apache.eve.backend.AtomicBackend.  All 
+            backends
+            
+            <footnote>
+                <para>
+                    Until now we loosely referred to a naming context's DIB as 
+                    a backend and will continue to do so.  For lack of a better 
+                    term, all AtomicBackend implementations are simply referred 
+                    to as backends in lowercase.  When we specifically refer to 
+                    the Backend interface, the 'B' in Backend will be 
+                    capitalized and followed by the word 'interface'.
+                </para>
+            </footnote>
+            
+            bound to an indivisible naming context implement the 
+            AtomicBackend service interface.  Indivisible implies that all 
+            entries within the context's namespace are stored within a single 
+            DIB: an implementation of AtomicBackend.  The UnifiedBackend in 
+            contrast unifies the namespaces of all AtomicBackends under one 
+            composite Backend.  The <ulink url="nexus-module.html">NexusModule
+            </ulink> documentation discusses the UnifiedBackend service 
+            interface and its implementation in more detail.
+        </para>
+        
+        <figure id="fig2">
+            <title>Figure II: Backend Interfaces</title>
+            <graphic fileref="../images/BackendInterfaces.gif"/>
+        </figure>
+        
+        <para>
+            The interface heirarchy differentiates common DIB operations from 
+            those that are specific to the UnifiedBackend and an AtomicBackend.
+            The Backend super interface contains methods common tto both
+            services.  Inspection of <link linkend="fig2">Figure II.</link>
+            reviels the differentiating methods between the two service 
+            interfaces which consequently defines their specific roles and their
+            behavoir.  An AtomicBackend has a suffix, or naming context root 
+            specified by a (non-empty string) LDAP distinguished name.  The
+            UnifiedBackend does not.  The UnifiedBackend cannot return a suffix
+            DN if it is composed of many naming contexts.  Which context root
+            would it return?  Furthurmore, it would be redundant to have a
+            getSuffix() method on the UnifiedBackend interface since the implied
+            suffix for the UnifiedBackend is the root of all roots: the empty 
+            string DN.  The AtomicBackend inherits method declarations from the
+            BackendConfig super interface through interface polymorphism and 
+            UnifiedBackend does not.  The BackendConfig interface defines 
+            standard configuration parameters applicable only to backends that 
+            directly store entries.  Configuration parameters like isReadOnly() 
+            does not make sense for a forwarding UnifiedBackend which delegates
+            entry and namespace operations to owning AtomicBackends.  Nothing 
+            makes the distinction more apparent than the methods exposed by the 
+            UnifiedBackend which register, unregister, list, and return 
+            AtomicBackends and the context's they are bound to.
+        </para>
+            
+        <para>
+            The Backend interface is the common super interface for all 
+            services providing DIB operations like nCRUD operations and 
+            namespace operations.  It's methods alone do not constitute a 
+            service.  Our intention was to allow for two drastically different
+            backend types while making their differences transparent through
+            the common Backend super interface.  This way conversations between
+            each AtomicBackend and a caller can be routed by way of the 
+            UnifiedBackend.
+        </para>
+            
+        <para>
+            With the differences clearly outlined between the core backend
+            interfaces, let's consider their simularities and some of the
+            common helper classes involved.  Close inspection reviels the
+            throws of NamingExceptions within virtually every core backend 
+            interface.  The throws directly reflect the close relationship
+            between the LDAP access model and the LDAP naming model.  These
+            two aspects of LDAP are inseperable.  All entries are named, 
+            possesing a unique distinguished name as defined by X.500 and 
+            LDAP.  Even the call to instantiate a new entry object via newEntry
+            requires a distinguished name.  Naming is inherent to LDAP and 
+            cannot be abstracted away from an LDAP DIB.  A name is required to
+            retrieve, change or store and search for an entry.
+        </para>
+            
+        <para>
+            Up until now, most methods declared by the Backend interface have
+            been explained, yet some return types from those methods acting as
+            helper classes have not.  Namely, mass read operations or searches 
+            return a Cursor.  Before looking at how we define a Cursor as part
+            of the server to backend contract lets consider the abstract notion
+            of a cursor.  The concept obviously arose from a database server's
+            need to conserve resources.  A database server may contain far more
+            data than can fit into physical memory, and must share whatever 
+            memory resources it has across concurrent clients.  Queries have the
+            potential to return a massive amount of data to the client.  For
+            these reasons databases do not have the option of buffering query 
+            results before transmission to the client.  If they do it must be
+            at an extremely minimal level, or it must use seconday disk memory
+            to do so.  Cursors are designed to return one search candidate at
+            a time streaming results back to the client.
+            
+            require the concept of a cursor.
+            The potential of returning the contents of the entire directory 
+            tree exists.  All entries cannot possibly be loaded into memory to
+            be returned to the client at any one time with anything but trivial
+            databases.  Cursors return values one at a time without requiring
+            the entire search set to be in memory at any one time.
+        </para>
+            
+        <para>
+            BackendModule simply implements some Avalon life-cycle methods out of the box on
+            behalf of the developer trying to write a new type of backend.  Our
+            architectural focus is not restricted to this class, however it's 
+            value add will be discussed within the implementation section of 
+            this document.  For architectural and implementation specific 
+            details regarding specific backend implementations available for the
+            LDAPd server please consult the backend specific documentation.
+            Below we list the various backends available, hyperlinked to their
+            documentation sets:
+        </para>
+        
+        <itemizedlist>
+            <listitem><para>
+                <ulink url="/../../../backend-blank.html">JDBM Module
+                </ulink> - Default non-relational module using the
+                <ulink url="http://jdbm.sourceforge.net">JDBM BTree</ulink>
+                implementation.  JDBM is a Java based UNIX DBM implementation.
+            </para></listitem>
+
+            <listitem><para>
+                <ulink url="/../../../backend-blank.html">Berkeley Module
+                </ulink> - Optional module using the
+                <ulink url="http://www.sleepycat.com">BerkeleyDB</ulink> 
+                non-relational database.  BerkeleyDB is a native C database that
+                uses an API to access BTree backed binary data.
+            </para></listitem>
+
+            <listitem><para>
+                <ulink url="/../../../module-blank.html">JDBC Module
+                </ulink> - Optional module using JDBC to store entries within a
+                relational database.
+            </para></listitem>
+        </itemizedlist>
+
+        <section>
+            <title>Entry CRUD Methods And DIB Operations</title>
+            
+            <para>
+                The Backend interface specifies entry Create, Read, Update and
+                Delete (CRUD) operations and various DIB operations which don't
+                quite fit into a strictly CRUD model.  Examples of such
+                operations would be move(), search() and listChildren().  The
+                Backend interface declares the minimum set of DIB operations
+                expected from a backend.  Notice in <link linkend="fig1">Figure 
+                I</link> that all Backend interface methods throw 
+                NamingExceptions or one of its subtypes.  The CRUD specific 
+                operations are listed below in Table I and the corresponding
+                lifecycle state transitions are depicted in Figure II below it:
+            </para>
+            
+            <para>
+                The CRUD
+            </para>
+        </section>
+    </section>
+
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+        </para>
+
+        <para>
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/client-module.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/client-module.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,496 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Client Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.6 $</revnumber>
+                <date>$Date: 2003/05/03 01:21:32 $</date>
+                <revdescription>
+                    <para>
+$Log: client-module.xml,v $
+Revision 1.6  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.5  2003/03/26 23:02:46  jmachols
+Added changes to ClientModule implementation to bring us towards a cleaner
+session and client manager.
+
+Revision 1.4  2003/03/26 22:48:54  akarasulu
+Factored some notes back into docs given to Jeff for changes to the Client
+Manager.
+
+Revision 1.3  2003/03/26 03:53:50  jmachols
+Documenting changes and future enhancements.
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.3  2003/03/15 17:38:49  bearcej
+cvs causing problems with xml processing
+
+Revision 1.1.2.2  2003/03/15 17:08:42  bearcej
+Add abstract tags around para for abstract description.
+Modified Files:
+ Tag: ALPHA-0_7
+    client-module.xml
+
+Revision 1.1.2.1  2003/03/10 23:24:19  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.2  2003/03/02 04:41:29  akarasulu
+*** empty log message ***
+
+Revision 1.1.2.1  2003/03/02 04:26:09  akarasulu
+ClientModule implementation document.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+    <para>
+        The ClientManager service is implemented by the ClientModule.  The
+        service is intended for client connection and hence session management.  
+        Session management plays a critical role in managing controls, user
+        profiles, authorization and the tracking of session variables.
+    </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add sequence diagrams for the handler.
+            </para></listitem>
+
+            <listitem><para>
+                Add diagrams to show some of the design concepts hit below
+                like the use of the ClientKey as a primary key, the use of
+                a thread context to pass it around etc.  Maybe a good sequence
+                diagram to show the steps to enable the storage of the ClientKey
+                within the thread context is worth while.  Drive this stuff home
+                with some diagrams because its really dry reading at the moment.
+                It puts me to sleep and I wrote it.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Design</title>
+        
+        <para>
+            Stateful protocols like LDAP maintain a client socket connection
+            through out the duration of a "session".  Several requests are 
+            issued within the scope of a session.  The socket connection
+            forms the basis to the concept of a client session.  The socket 
+            contains the IO streams and connection parameters which uniquely 
+            identify the connection.  These parameters are the client/server TCP
+            ports and the IP interfaces used to establish the connection.  
+            Connection parameters alone do not encapsulate all the information 
+            needed to complete the notion of an LDAP session.  Protocol oriented 
+            parameters specific to each client's session must be associated 
+            with the connection.  Such parameters include the identy of the
+            session owner, search controls and other operational modifiers.  As
+            stated within the objectives of the LDAPd project, we intend to 
+            provide the community with a rich LDAP application development 
+            environment on par with modern RDBMSs.  A session interface serves
+            as an exposed API, and it enables these goals by allowing user 
+            defined session variables to be read and written.  Our intent on 
+            differentiating between application, session and request scope 
+            parameters drives the design of the server and more specifically 
+            the role of the ClientManager in session state management.
+        </para>
+        
+        <para>
+            Several factors constrain our design.  Among these factors are good
+            architectural principals.  These constraints are itemized below:
+        </para>
+        
+        <itemizedlist>
+            <listitem><para>
+                Maintain a separation of concerns (SOC) to minimize coupling 
+                between subsystems.  This can be achieved by centralizing client 
+                management operations and isolating it within a single server 
+                module if possible or within the subsystem package of the 
+                module.
+            </para></listitem>
+
+            <listitem><para>
+                Recognize session management as a cross cutting aspect of any
+                stateful protocol server which counter acts SOC and increases
+                coupling.  Use design patterns to balance the affects of this 
+                pervasive aspect.
+            </para></listitem>
+            
+            <listitem><para>
+                Use the Inversion of Control (IOC) design pattern to protect
+                sensitive resources while prividing access to privledged 
+                components.
+            </para></listitem>
+
+            <listitem><para>
+                Balance these factors within a SEDA architecture with loosely
+                coupled events between stages.
+            </para></listitem>
+        </itemizedlist>
+        
+        <para>
+            The role of the ClientManager service revolves around the management
+            of clients, their connections and their session parameters.  Before
+            the introduction of core interfaces and classes used to establish 
+            the subsystem facade, the notion of a client and session requires a
+            clear definition.  To a stateful protocol server a client is a 
+            connection.  A process may posses multiple connections to the 
+            server.  Each connection regardless of the source is a client.  So
+            whether connections are made locally, from other hosts, from the
+            same process on a single host, all are treated as separate clients.
+            Each connection maintains session state on the server.  Session 
+            state stores session specific variables that are not directly 
+            related to defining or managing the connection.  For example one 
+            would not expect the identity of the authenticated user to be held
+            within a socket connection object.  The session encapsulates both
+            the connection parameters and other indirectly related session 
+            parameters.  All fall under the umbrella of a client session and are
+            inseperable.  The addition of a client implies the prior creation 
+            of a connection and the subsequent establishment of a client 
+            session.  The drop of a client implies the close of a socket 
+            connection and the destruction of the client's session.  
+            Consequently, all outstanding requests dependent on it are destroyed
+            as well.
+        </para>
+        
+        <para>
+            Design objectives revolve around the abstraction of the client and 
+            the session.  Several associations are loosely managed using a 
+            unique primary key to identify a client.  The final ClientKey class
+            defines the primary key.  It distinctly identifies a client based
+            on an identifier string constructed from connection endpoint 
+            parameters.  Clients may form connections eminating from any host
+            IP interface through any authorized port on that interface.  A 
+            server host may have multiple IP interfaces and the server process 
+            can chose to listen to any number of authorized TCP ports off those
+            interfaces.  Hence a client socket connection on the client host 
+            endpoint is defined by the IP interface and the TCP port used.  The 
+            client socket on the server side is defined by the server host's IP 
+            interface and the TCP ports of the interface on which the connection
+            is established.  Both sets of endpoint parameters are required to 
+            form a unique key distinctly identifing the client.  The primary key
+            identifier of the ClientKey is composed of these four parameters.
+        </para>
+        
+        <para>
+            Sessions and connections can be bound together using the client PK 
+            with associative tables or Maps.  The ClientSession interface 
+            defines the methods to be supported by a session entity.  
+            ClientSession implementation instances are assocated one to one with
+            a ClientKey, and a client Socket.  Depending on the object required
+            the key is used to access the respective Map to return the 
+            associated client resouce.
+        </para>
+        
+        <para>
+            The ClientKey is used as the handle on the client and its session. 
+            From it, associated objects can be looked up using ClientManager
+            methods.  Access to the key and to the ClientManager service are 
+            sufficent to apply operations affecting the state of the client
+            within the server.  For this reason every SEDA event except for the
+            ConnectEvent has a handle on the ClientKey object.  The key is 
+            passed along from stage to stage within the events.  Stage modules
+            that require the need to access client session state or affect 
+            changes to the client connection do so using this key and the 
+            ClientManager service interfaces.
+        </para>
+        
+        <para>
+            The ClientKey object is also critical for client IO synchronization 
+            within and between stages.  The OutputManager and the InputManager 
+            implementations must make sure that only one LDAP PDU is being 
+            recieved or delivered at one time.  A PDU may however be sent while
+            one is being recieved.  These requirements are accomplished by 
+            synchronizing on the ClientKey lock objects used for input and
+            output synchronization.
+        </para>
+        
+        <para>
+            The ClientKey nor the ClientSession or the ClientManager for that
+            matter, ever expose raw constructs used to manage the socket or IO
+            streams.  Meaning the actual client socket or its IO streams are not
+            accessible via API calls.  The encapsulation of the sockets and
+            its IO streams prevents tampering through public APIs.  It also
+            makes it harder for other stages to access these streams to read
+            or write to the client.  Services like the OuputManager and the 
+            InputManager need eventually to write and read from these streams
+            Application of the Inversion of Control design pattern facilitates
+            this without compromizing these sensitive resouces.  Both the 
+            InputManager and the OutputManager expose interfaces to register and
+            unregister a client socket's input and output stream respectively.  
+            The ClientManager is responsible for managing the [un]registration
+            process to enable and disable clients for IO within the server.  In
+            this respect the ClientManager controls what resources are made 
+            available to these IO managers.  It does not expose accessor methods
+            to access these IO streams by ClientKey so that IO managers can look
+            them up.  Doing so would publicly expose the streams to other 
+            unauthorized components within the server thereby bypassing 
+            encapsulation.  By passing only a stream and not the socket the 
+            ClientManager also prevents these IO services from interfering with
+            the physical socket connection.  If sockets were passed rather than
+            IO streams connected to the socket then the socket can be closed
+            without proper cleanup.  With separation of concern this is the 
+            responsibility of the ClientManager service and not that of an 
+            InputManager or an OutputManager.  Streams passed to these IO 
+            managers also need not be the socket streams.  They can instead be
+            wrapper streams that do not close the underlying data stream when
+            close operations take place.  Both managers together are responsible
+            for IO not the open or close of these connections.  Giving them the
+            power to do so would violate several design principals.  With IOC 
+            the IO managers can only mess up what they are expected to do and 
+            no more.  Other parts of the server never see these conduits to the 
+            client.  Only controlled and privledged access to these streams from
+            IO managers are possible.
+        </para>
+        
+        <para>
+            The ClientManager enables the addition of clients and the removal of
+            clients from the server using the add and drop service interfaces
+            methods.  These methods are intended to setup or tear down IO stream
+            registrations and handle session object intialization as discussed. 
+            The add method is called when a connection is initially established
+            even before a bind operation completes.  The session in this case 
+            has an LdapPrincipal with an empty yet valid Dn which connotates an
+            anonymous user.  When binding anonymously the empty string Dn is 
+            used.   The drop method is intended to close a client's connection
+            and invalidate it's ClientSession object.  The mappings used use to 
+            track the Socket by ClientKey are removed and the ClientKey is 
+            used to unregister the client's respective IO streams with both 
+            IO manager modules.  The ClientKey is also expired to prevent its 
+            use for any outstanding requests left in the stage pipeline.  Any
+            outstanding operations in progress or within the stage pipeline must
+            be terminated when the client is dropped: this is a requirement of 
+            the protocol.
+        </para>
+        
+        <para>
+            Some modules within the server are not stages so they do not recieve
+            events with payloads carrying ClientKeys.  Access to the 
+            ClientSession by way of ClientKey is only possible through method 
+            call arguments.  Between stages calls may be made by stage threads
+            to other simple modules which in turn call other simple modules.  
+            The method call chain can be very long especially if recursion is
+            used.  Having every simple module method pass in a ClientKey or 
+            ClientSession argument is impractical.  Just imagine to tunnel the
+            ClientSession down to one method it would have to be carried 
+            through multiple stack frames regardless of whether it is used in
+            the intermediate method call or not.  This would dramatically effect
+            readibility and confuse those trying to decipher the code.  
+        </para>
+        
+        <para>
+            The ClientManager service specifically confronts this issue which 
+            has the possibility of arrising in every stage of processing.  Two
+            interfaces are provided to associate and disassociate stage threads 
+            with ClientKeys.  The threadAssociate() method binds the ClientKey 
+            to the context of the calling Thread.  Subsequence calls to the no
+            argument getClientSession() method extracts the bound ClientKey 
+            from the calling Thread's context to use in a lookup that returns 
+            the associated ClientSession.  This way stages can bind the 
+            ClientKey of incomming events to the contexts of stage worker 
+            Threads right before event processing.  While stage Threads make 
+            calls to other non-staged modules synchronously in the context of
+            the stage worker Thread, the ClientKey will always accompany the
+            method call.  Simple modules use the no argument accessors to 
+            extract the ClientSession associated with the call without having
+            to pass around ClientKeys or handles to the ClientSession.  Once 
+            the stage worker Thread completes event processing it disassociates
+            the ClientKey from its context via a call to threadDisassociate()
+            on the ClientManager.  The facility is provided for all stages that
+            need to take advantage of this aspect of the service.
+        </para>
+        
+        <para>
+            With all these modules having dependencies on the ClientManager 
+            service cyclic dependencies between the ClientManager and other 
+            modules are unavoidable.  The ClientManager service in this sense
+            by far the most congested resource within the entire system.  
+            Microkernel containers like Phoenix, will not allow cyclic 
+            references between blocks or modules.  To avoid these pitfalls down
+            the road precautions were taken to define new channels through which
+            references can be made across modules in a well defined and 
+            constrained fashion without violating cyclic dependency rules within
+            an Avalon container.  Avalon uses the service method and the 
+            ServiceManager interface to facilitate the lifecycle stage where 
+            services acquire handles to other services they depend upon.  To
+            prevent the detection of true cyclic relationships between the the
+            ClientManager and other modules extra interfaces are defined.  These
+            interfaces are intended to be implemented by modules that depend on 
+            the ClientManager yet somewhere down the line the ClientManager 
+            either directly or indirectly depends on them.  So the goal is to
+            have these modules which would raise cyclic dependency errors in
+            Avalon containers obtain their backreference to the ClientManager
+            though a mechanism other than the regular Servicable life-cycle.
+            These modules should be clearly marked as imposing cyclic 
+            dependencies by way of the ClientManager.  Their access to the 
+            ClientManager service handle also needs to be controlled by the
+            ClientManager through Inversion of Control.  The ClientManager 
+            chooses who speaks to it and acts in some respect like a master 
+            module.  The dependent modules act like slaves.  The master slave
+            relationship becomes more apparent by investigating the 
+            ClientManagerSlave interface required of all modules depending on 
+            the ClientManager service.  There is one method that needs to be 
+            implemented which passes the handle to the ClientManager as the sole
+            argument.
+        </para>
+        
+        <para>
+            Up until this point we have explored the intimate details of the
+            ClientManager service role without regard to its implementation.  
+            Before going on to describe the current implementation we list the
+            responsibilities of the role:
+        </para>
+        
+        <itemizedlist>
+            <listitem><para>
+                The ClientManager ties together the various client resources 
+                composing the session of a client.  It does so through the use
+                of the unique ClientKey identifying the client.
+            </para></listitem>
+            
+            <listitem><para>
+                Add and drop methods enable control over the establishment of
+                new clients or closure to an existing client session both in 
+                respect to the socket connection and the session along with all
+                outstanding requests.  This way the service manages session 
+                creation and destruction.
+            </para></listitem>
+            
+            <listitem><para>
+                Services are provided to facilitate ClientKey propagation within
+                Thread contexts across non-staged server modules.
+            </para></listitem>
+            
+            <listitem><para>
+                The service enables a master slave relationship between itself 
+                and other dependent services to avoid cyclic dependency 
+                detection by Avalon containers.
+            </para></listitem>
+            
+        </itemizedlist>
+    </section>
+    
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+            The ClientManager interface implemented by the ClientModule extends
+            the ConnectListener interface.  Hence the ClientModule must 
+            implement the methods declared within ConnectListener.  
+            ConnectListener defines a single void method, connectPerformed, 
+            which takes as its sole argument a ConnectEvent.
+        </para>
+        
+        <para>
+            The signature of the connectPerformed method does not in any way
+            impose requirements on the way the ConnectEvent processing is 
+            implemented.  The ConnectListener implementation can handle the 
+            event asynchronously or synchronously.  The ClientModule is 
+            presently implemented as a stage so the connectPerformed method
+            is implemented as a simple synchronized enqueue operation on the 
+            event queue.  Processing of the event is handled asynchronously
+            using threads from the stage thread pool to drive the event handler.
+            For more information on general stage implementation see the SEDA
+            implementation document. (Make me a link!)
+        </para>
+        
+        <para>
+            ConnectEvents deliver client socket connections to the ClientManager
+            after a successful accept call on a server listner socket.  The 
+            event is enqueued on the stage event queue.  The stage handler 
+            thread awakens to dequeue the event, and hands off the event to a 
+            stage worker thread for processing.  During processing the event's 
+            Socket is extracted, and a unique ClientKey object is generated 
+            using the properties of the client's Socket.  The ClientModule 
+            generates the ClientKey from the Socket's TCP/IP connection 
+            parameters: the client interface, the client port, the server 
+            interface and the server connection port.  The add service method
+            is then called using the ClientKey and the Socket.
+        </para>
+        
+        <para>
+            The add() method implementation makes the association between the
+            ClientKey and the Socket for subsequent lookups.  It also registers
+            the Socket's input and output streams with the respective IO 
+            manager.  The add() method called by the stage worker Thread 
+            finishes off by creating a default ClientSession object for the 
+            client.  The default session uses a user Principal synonymous with 
+            an anonymous user.  The distinguished name is the empty string for
+            this default session.  Later bind operations replace the Principal
+            with the Principal of the authenticated user.  After creating the
+            default ClientSession it is bound to the ClientKey in a Map and 
+            returned.
+        </para>
+        
+        <para>
+            The association of ClientKeys within the context of the calling 
+            thread is made using a ThreadLocal instance within the 
+            threadAssociate() method.  The association enables the lookup of 
+            the ClientSession based on the Thread of execution.  If the 
+            executing Thread is mapped to a ClientKey within the ThreadLocal 
+            object then a getClientSession() call with no arguments easily 
+            retrieves the client's session without the ClientKey.  The 
+            getClientSession() method simply looks up the ClientKey associated
+            with the caller's Thread.  After isolating the the ClientKey a 
+            lookup on the session map using the ClientKey returns the 
+            ClientSession of the calling thread.  This way we avoid passing 
+            around ClientKeys or ClientSession handles in simple modules called 
+            between stages.  Furthermore because the ClientManager is the master
+            module that all other modules can refer to, localization of session 
+            data there is ideal.
+        </para>
+        
+        <para>
+            Why have we implemented socket connection accepts using an event
+            model while making the connection close or dropping of a client a
+            matter of a simple synchronous call? Why not use a DisconnectEvent
+            and a DisconnectListener? Well, this was the case at first but we 
+            found that the operation must proceed synchronously and there was
+            no need to have a stage for this task.  Clients need to be dropped
+            as soon as an unbind request completes destroying all other requests
+            in the pipeline.
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+            Create InputStream and OutputStream wrappers to disable close 
+            operations on these streams by the respective IO manager.  Right 
+            now the InputModule can close the client's input stream disabling
+            a client connection.  Likewise the OutputModule can do the same with
+            the OuputStream of the client socket connection.
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+            The module unnecessarily grabs a handle on the Decoder service.
+        </para>
+        
+        <para>
+            The module needs to correctly handle the change of user principals
+            by destroying all outstanding request operations and properly timing
+            out old handles to ClientKeys and ClientSessions.
+        </para>
+    </section>
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/controls.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/controls.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,124 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Controls</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.3 $</revnumber>
+                <date>$Date: 2003/05/03 01:21:32 $</date>
+                <revdescription>
+                    <para>
+$Log: controls.xml,v $
+Revision 1.3  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.2  2003/03/27 21:54:57  akarasulu
+Index exchange search control should be implemented.
+
+Revision 1.1  2003/03/27 21:34:14  akarasulu
+Start designs to implement some controls.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+    <para>
+        The LDAPv3 protocol uses controls to send and receive additional data 
+        to affect the behavior of predefined operations. Controls can be sent 
+        along with any LDAP operation to the server. These are referred to as 
+        request controls. For example, a "sort" control can be sent with an 
+        LDAP search operation to request that the results be returned in a 
+        particular order. Solicited and unsolicited controls can also be 
+        returned with responses from the server. Such controls are referred to
+        as response controls. For example, an LDAP server might define a 
+        special control to return change notifications.  LDAPv3 controls are
+        defined in section 4.1.12 of RFC <ulink url=
+        "http://www.faqs.org/rfcs/rfc2251.html">2251</ulink>. 
+    </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+
+            <listitem><para>
+                Implement special search index exchange control to redirect or
+                return entries from server with sufficent indices to rapidly
+                conduct the search.
+            </para></listitem>
+
+            <listitem><para>
+                Implement LDAP Control Extension for Server Side Sorting of 
+                Search Results, RFC 2891 
+            </para></listitem>
+
+            <listitem><para>
+                Implement LDAP Extensions for Scrolling View Browsing of Search 
+                Results, Internet-Draft Revision 4. 
+            </para></listitem>
+
+            <listitem><para>
+                Implement LDAP Proxied Authorization Control, Revision 5 
+            </para></listitem>
+
+            <listitem><para>
+                Implement LDAP Authentication Response Control, Internet-Draft 
+                Revision 1
+            </para></listitem>
+
+            <listitem><para>
+                Implement Persistent Search: A Simple LDAP Change Notification
+                Mechanism, Internet-Draft Revision 2 
+            </para></listitem>
+
+            <listitem><para>
+                Implement Manage DSA IT Control
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Design</title>
+        
+        <para>
+            Add design here.
+        </para>
+    </section>
+    
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+            Add implementation details here.
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+            Start this stuff.
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+            Did not even start.
+        </para>
+    </section>
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/decoder-module.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/decoder-module.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,258 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>DecoderModule Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.6 $</revnumber>
+                <date>$Date: 2003/05/03 01:43:08 $</date>
+                <revdescription>
+                    <para>
+$Log: decoder-module.xml,v $
+Revision 1.6  2003/05/03 01:43:08  bearcej
+Add maven file to help generate server site.
+Modify graphic links again.
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.5  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.4  2003/03/25 23:17:13  akarasulu
+Finished off doc changes for use of lock objects.
+
+Revision 1.3  2003/03/24 13:22:27  akarasulu
+Made it so all modules use ClientKey lock objects for IO locking.
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.3  2003/03/15 17:24:52  bearcej
+Fixed figure tags to conform to simple docbook.
+Modified Files:
+ Tag: ALPHA-0_7
+    decoder-module.xml
+
+Revision 1.1.2.2  2003/03/10 23:43:29  akarasulu
+Moved links so that image references point to /image/design.  Note that
+references do not have a [.] in front since the maven driven transforms
+automatically append [.] to references.
+
+Revision 1.1.2.1  2003/03/10 23:24:19  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.2  2003/03/06 23:11:26  akarasulu
+Proof read decoder module and added some content to protocol module docs.
+
+Revision 1.1.2.1  2003/03/03 04:39:08  akarasulu
+new docs and changes
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+            The DecoderModule implements the Decoder service interface which is
+            a subinterface of the InputListener interface.  The InputListener 
+            interface implements a single void method called inputReceived which
+            takes an InputEvent as its sole argument.  The DecoderModule is thus
+            a glorified InputEvent listener or processor which decodes BER 
+            encoded ASN.1 LDAPv3 request message envelopes into a request 
+            LDAPMessage object.  It essentially demarshals incomming requests 
+            and is implemented as a server stage.
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Have a link to the InputModule documentation.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+            As mentioned in the summary, the DecoderModule must implement only 
+            one service method: inputReceived.  The InputManager which is 
+            implemented as a stage directly upstream of this Decoder stage, 
+            has input handlers that monitor client Socket InputStreams for input
+            activity.  Once input is dectected an InputEvent is generated and
+            handed off to the inputReceived method of the Decoder service.  The
+            Decoder is then responsible for reading the request PDU from the
+            stream packaged within the InputEvent.  The InputModule only detects
+            the input without interfering with or removing the content from the 
+            stream.
+        </para>
+        
+        <para>
+            The DecoderModule implements the Decoder service as a stage and so
+            its inputReceived method simply enqueues the InputEvent onto the 
+            stage event queue and returns immediately.  The event is processed
+            asynchronously by the worker threads of the DecoderModule stage 
+            using the event handler initialized within the DecoderModule's 
+            default constructor.  Events are dequeued by the stage's driver 
+            thread, and the code within the AbstractStage super class for this 
+            module creates an annonymous Runnable to wrap and drive the call to
+            the stage's EventHandler using the dequeued event.  For more details 
+            regarding stage processing see the architecture and implementation 
+            documentation for SEDA stages in LDAPd.
+        </para>
+        
+        <figure>
+            <title>DecoderModule's InputEventHandler Sequence Diagram</title>
+           <graphic fileref=
+       "../images/InputEventHandlerHandleEvent.gif"/>
+        </figure>
+        
+        <para>
+            The EventHandler for the DecoderModule is implemented as a named
+            inner class extending AbstractEventHandler.  It is responsible for
+            processing the InputEvent in its handleEvent method which takes an
+            EventObject.  The event argument must be an instance of an 
+            InputEvent.  The handleEvent method performs the work which is
+            driven by a stage worker thread.  handleEvent extracts from the 
+            InputEvent argument the ClientKey and an InputStream.
+        </para>
+        
+        <para>
+            The extracted InputStream is used to read and decode BER encoded 
+            ASN.1 LDAPv3 request message envelopes.  The DecoderModule uses a 
+            Snacc4J BERDecoder to demarshal the encoded stream bytes into an 
+            LDAPMessage request envelope object.  The Snacc4J compiler was 
+            applied to generate this LDAPMessage stub class and other classes 
+            whose instances are kept within a containment heirarchy with the
+            LDAPMessage at the root.  The Snacc4J stub compiler generated these 
+            classes from the LDAPv3 definitions specified in ASN.1 notation in 
+            <ulink url="http://www.faqs.org/rfcs/rfc2251.html"> RFC 2251</ulink> 
+            Appendix A.  The generated classes are stored within the 
+            ldapd-common subproject under the org.apache.ldap.common.ber.ldap_v3 package.
+            These classes are kept within the common subproject so that command
+            line ldap clients can be written both for protocol test suites and
+            production use.  The Snacc4J BERDecoder instance is created to 
+            specifically read from the InputStream extracted from the InputEvent
+            by invoking its constructor using the InputStream as the sole 
+            argument.  Then within a synchronized block on the input lock, the 
+            decode method of the LDAPMessage object is called using the 
+            BERDecoder instance as the sole argument.  The LDAPMessage instance 
+            was created earlier as an empty message void of content using the 
+            default constructor. Once the decode method of the LDAPMessage 
+            returns, the entire request PDU will have been read from the 
+            client's InputStream and the LDAPMessage instance will contain a 
+            tree of nested LDAPv3 defined object instances.  At this point, 
+            any further incomming bytes on the InputStream are the bytes to the 
+            next request being sent by the client.  For more info on how Snacc4J 
+            reads and decodes bytes on the InputStream to generate the 
+            LDAPMessage containment tree consult the 
+            <ulink url="http://www.alphaworks.ibm.com/tech/snaccforjava">
+            Snacc4J website</ulink>.  Since protocol engine request processors 
+            must directly access the contents within the LDAPMessage envelope,
+            the structure of the containment tree is discussed in more detail 
+            within their documentation.
+        </para>
+        
+        <para>
+            All modules must assume the possibility of concurrent access to 
+            client resources.  In the case of the Decoder another thread may
+            attempt to read from the client InputStream while a request 
+            extraction is in progress.  Concurrent access would corrupt the
+            content stream removing bytes from the PDU.  The Decoder must make
+            certain that no other thread is reading from the client's input 
+            stream from which the decoder may be in the process of demarshalling
+            a request message.  The ClientKey was explicitly designed to 
+            centralize lock objects used for inter and intra module 
+            synchronization.  Lock objects for both the client's input and 
+            output channels exist within the ClientKey.  Two separate lock 
+            objects were intentionally used to enable the writing of a response 
+            while reading requests from the client at the same time.  All 
+            modules must use these lock objects to synchronize client IO 
+            instead of using the supplied streams carried by events.  The
+            objects carried by an event may not be those used for 
+            synchronization in other modules.  Use of these lock objects 
+            instead guarrantee the use of the correct synchronization object.
+        </para>
+
+        <para>
+            Before leaving the synchronized block on the lock object, the 
+            notifyAll method is called on the input lock.  This wakes up blocked
+            threads waiting to read from the clients InputStream.  The client 
+            input monitor thread in the InputModule stage waits for this notify 
+            to resume reads on the InputStream when the decoder is finished 
+            demarshalling a request.  Finally, with the LDAPMessage populated 
+            a RequestEvent is created and packaged with the ClientKey and the 
+            demarshaled LDAPMessage.  Before returning from the handleEvent 
+            method in the InputEventHandler the generated RequestEvent is 
+            delivered to the ProtocolEngine to process the request.  The 
+            requestReceived method on the ProtocolEngine is used with the newly
+            created RequestEvent as the argument.  Upon return the worker thread
+            dies and is reclaimed by the worker thread pool of the 
+            DecoderModule stage.
+        </para>
+    </section>
+    
+    <section id="future">
+        <title>Future</title>
+        
+        <itemizedlist><listitem><para>
+            Think about writing a hard coded LDAPv3 specific encoder and decoder 
+            using our own ASN.1 parser.  The current stub compilers generate
+            stubs that implement or extend generalized encodind and decoding 
+            classes to handle any kind of notation.  This makes them less 
+            efficient but more useful.  If we hard code only for the the 
+            LDAPv3 notation and use JDK1.4 new IO libraries we can customize and
+            optimize the encoder and decoder IO handling.  Also the containment 
+            tree generated by snacc in a decoded LDAPMessage is very mesy from 
+            an OO perspective.  All members are public and intuitive accessors 
+            are not provided.  Using these generated APIs for PDU construction 
+            is tedious and the code is hard to maintain.  Also collection APIs
+            are not used in Snacc4J Java stubs.  A lot of ugliness within the 
+            protocol processors would go away if we coded explicity for the 
+            LDAPv3 definitions.  The undertaking however is an open source 
+            project on its own but well worth it considering the prevalence use
+            of ASN.1 and the BER, DER, and XER encodings.  Considering the 
+            performance impact of using direct memory IO on incoming and 
+            outgoing messages for decoding and encoding buffers gets us excited.
+        </para></listitem></itemizedlist>
+
+        <itemizedlist><listitem><para>
+            Consider the use of a2j rather than Snacc4J.  IBM's Snacc4J license 
+            according to Peter Donald is not very compatable with the Apache 1.1
+            license.  The a4j ASN.1 to Java Class compiler would better 
+            compliment the Apache License.  a2j is available 
+            <ulink url="http://sourceforge.net/projects/a2j">here</ulink>, but 
+            it may not be mature enough for now.
+        </para></listitem></itemizedlist>
+
+        <itemizedlist><listitem><para>
+            Potential use of new IO APIs to decode.  Will this be necessary
+            if channels are interchangeable or wrappable by InputStreams.  We
+            will always have to generate a DOM like containment tree of the 
+            request message to access it.  Perhaps a SAX like implementation 
+            can be found but will that be feasible with the manner in which 
+            request processors operate.  Do we want to cache copies of this 
+            data in multiple formes? Answer is no way.  Right now it is stored
+            in the LDAPMessage tree as well as parameters within the protocol
+            request processors.  It would be nice to just maintain one copy of
+            the information set stored in each request.
+        </para></listitem></itemizedlist>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <itemizedlist><listitem><para>
+            Uses Snacc4J.
+        </para></listitem></itemizedlist>
+    </section>
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/encoder-module.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/encoder-module.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,165 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.6 $</revnumber>
+                <date>$Date: 2003/05/03 01:45:23 $</date>
+                <revdescription>
+                    <para>
+$Log: encoder-module.xml,v $
+Revision 1.6  2003/05/03 01:45:23  bearcej
+Refix image URLs
+
+Revision 1.5  2003/05/03 01:43:08  bearcej
+Add maven file to help generate server site.
+Modify graphic links again.
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.4  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.3  2003/04/08 02:29:55  bearcej
+* Clean up project.properties
+* Fix logos in project.xml
+* Fix links to image files in docs
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.4  2003/03/15 17:34:19  bearcej
+Fix href to ResponseEventHandlerHandleEvent.gif
+
+Revision 1.1.2.3  2003/03/15 17:25:35  bearcej
+Fix figure tags to conform to simple docbook.
+Modified Files:
+ Tag: ALPHA-0_7
+    encoder-module.xml
+
+Revision 1.1.2.2  2003/03/10 23:43:30  akarasulu
+Moved links so that image references point to /image/design.  Note that
+references do not have a [.] in front since the maven driven transforms
+automatically append [.] to references.
+
+Revision 1.1.2.1  2003/03/10 23:24:19  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.2  2003/03/09 00:36:54  akarasulu
+Finished encoder docs and refactored inner annonymous handler to be a
+named inner class.
+
+Revision 1.1.2.1  2003/03/03 04:49:25  akarasulu
+Added as placeholders for now.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+            The EncoderModule implements the Encoder service interface as a 
+            stage within the server.  It asynchronously processes ResponseEvents
+            in parallel, encoding the contents of the response message envelope
+            packaged into the ResponseEvent.  Effectively the encoder is 
+            responsible for mashaling the contents of the LDAPMessage response
+            envelope filled with Snacc4J compiler generated class instances into
+            a byte buffer.
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+            The Encoder service interface extends the ResponseListener interface
+            besides defining extra synchronous processing methods.  The single
+            ResponseListener method, responseComposed(), gives the staged module
+            its event processing character.  The method accepts a ResponseEvent
+            as its argument.  The implementation simply enqueues the event onto
+            the stage event queue and returns immediately.  The stage driver 
+            thread as in all stages with the server, dequeues the event and
+            dedicates a worker thread to process the event.  Again a 
+            intermediate annonymous Runnable implementation is used to have the
+            worker thread drive the call to the stage event handler.
+        </para>
+        
+        <para>
+            A single method is directly defined within the Encoder interface 
+            rather than being inherited from ResponseListener: the encode 
+            method.  The encode method is implemented by the module as a 
+            synchronous call to encode a message.  Meaning a worker thread does
+            not drive the marshaling process.  The thread of the caller drives
+            the encoding.  This synchronous version requires no event hence it
+            is not part of the ResponseListener interface but is defined 
+            directly within the Encoder interface.  The upstream need within the
+            search request processor to synchronously deliver search entry 
+            response message envelopes has resulted in this synchronous method,
+            encode.  It is only utilized within the search request processor of
+            the protocol engine module.
+        </para>
+        
+        <para>
+            Upon the dequeue of a ResponseEvent and the subsequent assignment 
+            of a worker thread to process the event within the event handler,
+            the real work of marshaling the response message envelope can be 
+            done.  A named inner class, ResponseEventHandler, is used to 
+            implement the stage event handler.  The handleEvent() method of this
+            class is used to process the ResponseEvent in the execution context
+            of a stage worker thread.  Below is a sequence diagram showing the
+            high level steps in processing the ResponseEvent within the event
+            handler.
+        </para>
+        
+        <figure>
+            <title>EncoderModule's ResponseEventHandler Sequence Diagram</title>
+            <graphic fileref="../images/ResponseEventHandlerHandleEvent.gif"/>
+        </figure>
+        
+        <para>
+            The client key of the client which generated the ResponseEvent and 
+            the response message envelope are first extracted from the event 
+            argument.  The message is encoded into a byte buffer by calling the
+            synchronous encode method.  Then the resultant marshaled PDU in the
+            byte array is used along with the client key to construct an 
+            OutputEvent.  Before the handler completes and the driving worker 
+            thread dies, the event is delivered to the downstream OutputManager
+            stage to be processed asynchronously via the writeResponse() method
+            on the OutputManager service interface.
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+            The same future enhancements mentioned for the decoder hold for 
+            the encoder.
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+            Find some faults! Believe me you they exist.
+        </para>
+    </section>
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/event-module.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/event-module.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,343 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.3 $</revnumber>
+                <date>$Date: 2003/05/03 01:21:32 $</date>
+                <revdescription>
+                    <para>
+$Log: event-module.xml,v $
+Revision 1.3  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.1  2003/03/10 23:24:20  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.3  2003/03/09 22:22:35  akarasulu
+For all practical purposes the EventModule documentation is considered
+done.  It is not really used yet and when it is we can fill in the
+relavent sections.
+
+Revision 1.1.2.2  2003/03/09 22:12:48  akarasulu
+Moved the event-model-chapter.xml content into the event-module.xml file.
+
+Revision 1.1.2.1  2003/03/03 04:49:25  akarasulu
+Added as placeholders for now.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+            This chapter discusses ldapd's server side protocol event model, 
+            its use, design and implementation.  It does not cover the SEDA 
+            event model that loosely couples stages within the server.  For lack
+            of a better term we refer to this system of events, listeners and 
+            sources as the protocol event model.
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>
+            Potential Uses For Pre And Post Protocol Events
+        </title>
+        
+        <para>
+            During the course of processing LDAP requests, subsystems will need 
+            to transparently inject their services.  An event model provides a 
+            decoupled way for these systems to communicate.  Listeners abstract 
+            and decouple interfaces on targets while source interfaces used to 
+            register listeners decouple the event source.  Events simply carry 
+            payloads of information required to respond appropriately.  Several 
+            subsystems will leverage the event model to transparently effect 
+            server operations without impossing dependence between themselves 
+            and other modules within the server.  This section discusses various
+            systems that would benefit from an event model.
+        </para>
+    
+        <para>
+            Authorization is an excellent candidate for using the event model.  
+            When registered for events, the authorization module will recieve 
+            events with profile information and other context information 
+            describing the operation being performed.  Of course it makes no 
+            sence to have the authorization module recieve these events after an
+            operation completes.  So it must recieve pre events to actually 
+            intervene in the process, thereby preventing unauthorized requests 
+            from being serviced.  Event delivery must notify synchronously of 
+            the pending event to allow for the authorization module to 
+            intervene.
+        </para>
+        
+        <para>
+            A trigger subsystem or module would also depend on an event model.  
+            Triggers in the RDBMS world are specified using a set of operations 
+            with a modifier used to determine when the trigger fires.  Triggers
+            this way can fire before or after an operation takes place.  
+            Triggers communicate their objections to the rest of the system by 
+            raising exceptions when conditions are not met.  Some triggers are 
+            required after an operation has completed.  Regardless of the 
+            purpose of a trigger, correct trigger operation is dependent on the 
+            receipt of events by the trigger subsystem before and after protocol 
+            operations occur.
+        </para>
+        
+        <para>
+            Master servers with replicas need to recieve entry change 
+            notification events.  Master servers contain a master replication 
+            module.  This module enqueue's change events onto the replication 
+            queues of the replication subsystem.  These events are used to 
+            asynchronously communicate the changes that need to take place to 
+            keep replica servers synchronized with master server DIB (Direcotry 
+            Information Base) changes.
+        </para>
+    </section>
+
+    <section>
+        <title>
+            Aspects Of The Protocol Event Model
+        </title>
+        
+        <para>
+            When discussing events, we need to qualify their delivery as 
+            synchronous or asynchronous.  Different situations may warrent 
+            different delivery timing requirements.  Events are triggered by 
+            event sources, then delivered to targets that posses event listener 
+            handler methods.  The design of a protocol event model requires the 
+            clear definition of the events, targets and sources involved.  These
+            concerns and their details must be explored for all conceivable use 
+            cases for the protocol event model.
+        </para>
+        
+        <para>
+            Listeners registered to receive events often need to effect the 
+            outcome of an operation based on some condition.  The authorization 
+            module is a good example of such a listener.  It must check to see 
+            if each protocol operation is allowed to proceed based on the 
+            authorization profile of the user and the parameters of the 
+            operation.  If for some reason the user is not allowed to perform 
+            the operation, the authorization subsystem must stop the operation.  
+            To stop the execution of an operation the delivery of the event must
+            occur before the operation begins.  The operation must be kept in a 
+            suspended state until the authorization module can evaluate whether 
+            access is granted or denied.  The situation requires synchronous 
+            event delivery.  If the event is delivered synchronously before the 
+            operation begins, the operation waites until the event handlers of 
+            the listeners complete - meaning until the authorization module 
+            evaluates the operation.  With synchronous deliveray a runtime 
+            authorization exception can be raised to stop the current executing 
+            thread.  The exception tunnels back up the exception stack of the 
+            driving processor thread.  The thread driver traps all exceptions 
+            reporting the reason for the failure by packaging the contents of 
+            the authorization exception into a return message to the client.
+        </para>
+    
+        <para>
+            Synchronous event delivery best suites the needs of the 
+            authorization module.  The module easily prevents an operation from 
+            executing by throwing an exception.  Because the thread executing 
+            the operation is the same thread calling the listener handlers, an 
+            exception thrown by a handler interupts the operation.  Most use 
+            cases for an event model are satisfied by a synchronous delivery 
+            mechanism.  Synchronous delivery of events can be handled 
+            asynchronously by target modules.  For example replication is best 
+            handled asynchronously.  Upon receiving an event that represents the
+            alteration of a mastered DIB the master replication module may use 
+            another thread to asynchronously push these events onto a 
+            replication event queue.  The delivery of the protocol event is 
+            synchronous yet how it is handled becomes asynchronous in the 
+            implementation replication module.  Synchronous event delivery does 
+            enable listeners to alter the outcome of an operation.  This is 
+            great if you need to affect the outcome.  This is not so good if you
+            do not want to give such power over the outcome to listener 
+            handlers.
+        </para>
+    
+        <para>
+            The power to effect the outcome or delay the outcome is given to 
+            every listener handler invoked by a processing thread.  Conditional 
+            traps can however be used to catch exceptions and ignore them, 
+            thereby allowing the operation to continue regardless of bad 
+            listener code or listener code objecting to the operation.  This 
+            would enable a finer degree of control over how much power is handed
+            off to the event handlers of registered listeners.  Modifiers that 
+            determine the mode of operation for the registered listener would be
+            part of a listener specification.  In terms of delaying the outcome,
+            there is very little that can be done outside of using processor 
+            thread controls similar to those used for search controls.  For 
+            example time limits are used to constrain a search request.  Similar 
+            constructs can be used to contstrain the maximum amount of time 
+            allowed for a thread to spend processing an event.  If the the 
+            thread spends too much time in listener event handlers the operation 
+            can be abandoned.  Again parameters used to limit the amount of time
+            a thread can spend in the listener handlers should be part of a 
+            listener specification defined perhaps at listener registration 
+            time.  Perhaps the maximum time limit can be specified based on the 
+            event type delivered instead of the listener registered.  In this 
+            case the delivery specification must be given with every event 
+            fired.  There are several ways in which the behavior of synchronous 
+            event delivery can be managed but overall synchronous event delivery 
+            is the best way to go for our internal protocol event model.
+        </para>
+    
+        <para>
+            Events can be fired from anywhere and delivered to any listener in 
+            any order.  There really is no difference between a synchronous 
+            event delivery method call and any other method call.  Event 
+            listeners simply comply with an interface for recieving a handle on
+            a constant event delivered to each listener.  These listeners are 
+            called one after the other by the same thread in a firing loop.  
+            Any module or operation in the server may initiate the firing of an 
+            event.  The firing component is logically the source of the event, 
+            in that it drove the creation of the event and triggered its 
+            delivery to the target listener interface method.  The source may 
+            be coming in from anywhere.  Obviously the protocol module can be
+            the source, the JNDI provider can also be the source when the JNDI
+            interfaces are used to make changes.  The thread used to drive the 
+            change obviously comes from the protocol module as a processor 
+            thread.  This will undoubtedly change as we begin to enable timer 
+            driven triggers in the server which are scheduled to run stored
+            procedures as a particular user.  These will be driven by a timer
+            thread or a thread from a central pool.
+        </para>
+        
+        <para>
+            Any caller to a listener handler method has the potential to be an 
+            event source.  How then do we control and manage event sources and 
+            event listeners?  We cannot and should not constrain event inducing
+            sources because the picture may change with time as parts of the 
+            server are improved.  Sources may change with server module 
+            configurations and so should be left undefined.  We should however
+            constrain listeners to implement specified interfaces.  New 
+            listeners developed can easily implement these interfaces which 
+            specify the event types listened for.
+        </para>
+        
+        <para>
+            As discussed listeners will need to register themselves with ideally 
+            the event source that generates and delivers the event.  This 
+            however need not be the case.  With many event models the source 
+            requests delivery of events to listeners using a third party which 
+            is a central fixture.  Sure listeners are registered at the source
+            for events but this need not be the case either.  A central 
+            registry can be used to act as a hub for all events generated in 
+            the server.  The registry provides interfaces for listeners to 
+            register for events.  It can also provide interfaces for firing 
+            various events on behalf of the event source.  Under such a
+            configuration we would centralize the machinery for the lookup of 
+            listeners and the delivery of events based on the event source.  
+            All the timing based requirements of event delivery and the traps 
+            used to prevent listener interference can be implemented there in
+            a central event manager.  Note once again this is separate from the 
+            asynchonous events used across server stages.
+        </para>
+        
+        <para>
+            Events are modeled to represent protocol operations.  LDAP abandon, 
+            add, bind, compare, delete, modify, modify DN, search and unbind
+            operations are represented using their own respective event types 
+            that derive from one common abstract ProtocolEvent ancestor.  The 
+            ProtocolEvent is the abstract based where common functionality or 
+            data can be added to the set of events in the model.
+        </para>
+            
+        <para>
+            Protocol operations may not directly be driving the firing of 
+            events in all cases.   Scheduled maintainence operations triggered
+            by a timer may fire events as a consequence of executing the stored
+            procedures they invoke.  These stored procedures induce changes
+            through the internal server side JNDI provider. No LDAP operation
+            recieved from a client as a PDU (Protocol Data Unit) is associated 
+            with the events fired by the executing stored procedure.  These 
+            phantom events are flagged as such via a boolean property.  This
+            property determines whether or not a physical PDU was delivered 
+            for the event.  Client operations against the server may raise 
+            triggers that fire stored procedures which operate on a DIT within
+            the server.  These indirect trigger driven changes will fire
+            events that are not associated with a physical PDU and hence will
+            have the respective flag set to false.  These events are part of 
+            the event cascade centrally rooted at the original event that 
+            started the cascade.  We need to differentiate between the root 
+            of an event cascade and all other descendent events that may fire
+            as a result of the original root event.  Protocol events are 
+            designed to keep a handle on a root event.  This way the event 
+            cascade tree can be represented using parent child associations.  
+            The parent event this way is represented as having triggered a
+            child in a cascade of event firings.  The root event of the cascade 
+            refers back to itself as the cascade root and also offers an 
+            interface to check if it is the root of the cascade this way.  In
+            both of these cases (scheduled firing and indirect cascade driven 
+            firing) no protocol activity exists yet protocol events will be 
+            fired as if a comparable protocol operation were inducing the event.
+            The event model captures all the information needed by a listener 
+            to differentiate how and why the event was fired.  By determining
+            if the event is or is not the root of a cascade and if it is
+            directly associated with a physical PDU listeners can take the
+            appropriate action after interpreting the context of the event.
+        </para>
+        
+        <para>
+            Often one event may trigger another event to be fired.  At times 
+            this will naturally result in an event cascade which may be a 
+            desirable side effect.  These cascades may result in unbounded 
+            cyclic firings within the server if listener event handlers are not
+            carefully designed.  The server must detect these dangerous
+            conditions and halt the firing of subsequent events.  Luckily since
+            synchronous delivery is used we can track the tree of events 
+            representing the event cascade by associating the structure with
+            the driving thread.  Using this tree carried with the thread of
+            execution we can determine if a cyclic firing is occuring and 
+            terminate the cascade before a StackOverflowError results.  A 
+            central event manager is perfect for placing such code to detect 
+            unbounded event cascades and managing a weak thread context hash 
+            for the event cascade tree structure.
+        </para>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+            Add implementation details here.
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+            Add future enhancements here.
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+            Add faults here.
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/input-module.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/input-module.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,213 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Input Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.6 $</revnumber>
+                <date>$Date: 2003/05/03 01:45:23 $</date>
+                <revdescription>
+                    <para>
+$Log: input-module.xml,v $
+Revision 1.6  2003/05/03 01:45:23  bearcej
+Refix image URLs
+
+Revision 1.5  2003/05/03 01:43:08  bearcej
+Add maven file to help generate server site.
+Modify graphic links again.
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.4  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.3  2003/03/24 13:22:27  akarasulu
+Made it so all modules use ClientKey lock objects for IO locking.
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.3  2003/03/15 17:11:40  bearcej
+Fixed figure tags to conform to simple docbook.
+Modified Files:
+ Tag: ALPHA-0_7
+    input-module.xml
+
+Revision 1.1.2.2  2003/03/10 23:43:31  akarasulu
+Moved links so that image references point to /image/design.  Note that
+references do not have a [.] in front since the maven driven transforms
+automatically append [.] to references.
+
+Revision 1.1.2.1  2003/03/10 23:24:20  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.2  2003/03/03 04:39:08  akarasulu
+new docs and changes
+
+Revision 1.1.2.1  2003/03/02 22:29:22  akarasulu
+Added input module architecture and implementation document.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+            The InputModule implements the InputManager service interface and in
+            doing so is responsible for detecting input on the client Socket's 
+            InputStream.
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+            The InputManager service interface implemented by this module 
+            defines two symetric methods for client input management.  These
+            methods are register and unregister.  The register method takes two
+            arguments: the client socket's ClientKey and its InputStream.  The 
+            unregister method takes a sole ClientKey argument to undo the 
+            affects of the register method.  Through these interface methods the
+            service provides input detection on registered client InputStreams.
+            Detection of incomming client requests results in the creation and
+            delivery of InputEvents to the request Decoder.  
+        </para>
+        
+        <para>
+            The InputModule is not a staged module.  Hence stage events can not 
+            be enqueued onto this module. Unlike a simple module it has an 
+            internal thread pool.  Pool threads execute the code which detects 
+            input on each client socket InputStream.  
+        </para>
+        
+        <para>
+            The monitoring process begins when clients are registered.  The 
+            register call first maps a ClientKey argument to an InputStream 
+            argument using a Map.  The Map enables rapid InputStream lookups 
+            based on ClientKeys.  The register method then creates and 
+            instantiates an InputStreamMonitor which happens to implement the
+            Runnable interface.  The InputStreamMonitor's run method contains 
+            the logic used to detect input on the client Socket's InputStream 
+            and is executed by threads from this module's sole thread pool.  
+            The register method, immediately before returning, assigns and 
+            starts a Thread from the pool to drive the Runnable 
+            InputStreamMonitor.  Hence an InputStreamMonitor, a driving thread,
+            and a ClientKey to InputStream Map entry are created for each 
+            registered client.
+        </para>
+        
+        <figure>
+            <title>register() Method Sequence Diagram </title>
+            <graphic fileref=
+                "../images/InputModuleRegister.gif"/>
+        </figure>
+        
+        <para>
+            The Runnable InputStreamMonitor waits blocked on a 
+            PushbackInputStream.  The PushbackInputStream wraps the client 
+            Socket's InputStream so reads from it actually read from the client.
+            The blocking call is on the read() method without arguments which 
+            returns a single byte of data from the underlying InputStream. When 
+            read() returns the byte read is pushed back to enable the decoder
+            to read a complete protocol request.  An InputEvent is created in
+            response using the ClientKey with the PushbackInputStream as event
+            members.  The InputEvent is then delivered to the Decoder service
+            which extends the InputListener interface.  The InputListener method 
+            used to deliver the event is inputReceived() which takes a sole 
+            InputEvent argument.  Once the Decorder extracts the PDU from the 
+            PushbackInputStream the monitor then loops once again to listen 
+            for incomming request PDUs.  The process continues for each request
+            PDU delivered to the client.  If the client disconnects abruptly 
+            an IOException occurs.  Once caught the client is dropped and the
+            loop terminates by returning from the run method.  Dropped clients
+            are handled by testing for key expiration or responding to a 
+            KeyExpiryException.  In either case the InputStreamMonitor's loop
+            terminates and the run method returns.
+        </para>
+        
+        <para>
+            The run() method synchronizes on the input lock object of the client
+            before going into the while loop which detects input on the stream.
+            This is necessary to prevent reads while a decoder extracts the PDU.
+            The loop makes sure that it does not reread from the stream on the 
+            same incomming PDU after input detection by sitting in a wait state.  
+            The indefinate wait() is called on the input lock object after the 
+            event is handed off to the Decoder.  The worker thread is awakened
+            to continue monitoring for input after the Decoder completes the 
+            full read of the request PDU.  The Decoder awakens the worker by
+            calling notifyAll() on the input lock object immediately after the
+            PDU read.
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <itemizedlist><listitem><para>
+            Because blocking IO is used, a thread pool is needed to assign a 
+            thread to a Runnable input handler to monitor each client Socket's 
+            InputStream.  This is unavoidable with any JVM below version 1.4
+            since non-blocking IO is not provided.  JDK 1.4 introduces the new
+            IO packages that have selectable IO channels where a single thread
+            can be used to monitor multiple input channels.  For a stateful
+            protocol server non-blocking IO is critical otherwise a thread is
+            required for the management of IO on each client.  Stateful protocol
+            servers (like LDAPd) can NOT scale well as the number of concurrent 
+            clients increase.  This was a serious limitation for pure Java
+            stateful protocol servers up until JDK1.4.  Native libraries
+            could have been used while sacrificing portability but this would 
+            have problems with Java purists.  With a one to one ratio of clients
+            to threads, performance will degrade rapidly after 50 clients.
+            Non-blocking IO should allow for thousands of concurrent client 
+            connections with minimal performance hits.  Furthermore the overhead
+            of SEDA in LDAPd will not pay off until non-blocking IO is used.
+            Expect the use of NIO libraries or their equivalent in a beta 
+            release.
+        </para></listitem></itemizedlist>
+
+        <itemizedlist><listitem><para>
+            We have specifically modeled the modules dealing with client IO as
+            granular as possible to reflect a separation of concerns.  Rather 
+            than use larger modules to handle the detection and read from a 
+            stream we created two separate modules: one for IO detection and 
+            another to actually read from the client's stream.  The same
+            approach was applied with respect to the detection of client
+            connections on the server socket.  If client connection accepts,
+            input detection and input reads were bundled into one module, the
+            implementation would have been extremely trivial yet very coupled
+            and hard to manage without complete rewrites.  The listener, input, 
+            output, encoder, decoder and client modules have been defined to 
+            handle a small part of client IO management functions.  This 
+            approach should make it easier to implement plugable non-blocking 
+            replacements for most of these modules with minimal changes to their 
+            service interfaces or affecting other services associated with IO
+            management.
+        </para></listitem></itemizedlist>
+    </section>
+        
+    <section>
+        <title>Faults</title>
+        <itemizedlist>
+            <listitem><para>
+                Does not presently use non-blocking IO.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/jndi-provider-module.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/jndi-provider-module.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,169 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+	"http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+	<title>Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: akarasulu $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.4 $</revnumber>
+                <date>$Date: 2003/08/22 21:15:54 $</date>
+                <revdescription>
+                    <para>
+$Log: jndi-provider-module.xml,v $
+Revision 1.4  2003/08/22 21:15:54  akarasulu
+Merged changes made in the USING_PROVIDER_FRAMEWORK branch of the
+server.  I don't suspect she will be working well at this point.  Lots
+of overhauling required now.
+
+Revision 1.2.2.1  2003/05/19 00:42:41  akarasulu
+Working on getting better integration with JNDI and embedding result codes
+into the exception message strings.
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.1  2003/03/10 23:24:20  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.1  2003/03/03 04:49:25  akarasulu
+Added as placeholders for now.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+	<abstract>
+		<para>
+		</para>
+	</abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+        </para>
+
+        <para>
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+    <section>
+        <title>Notes</title>
+        
+        <para>
+            When the JNDI provider is used directly outside of the path through
+            the protocol module, there is no ClientKey or ClientSession 
+            associated with the thread of execution.  One cannot use the client
+            key to access the ClientSession through the ClientManager interface.
+            The identity of the user is unknown currently.  Code responsible for
+            setting the operational attributes within the UnifiedBackend or 
+            nexus as it is referred to, requires the prinicipal of the user in
+            order to set attributes like the creatorsName and the modifiersName.
+            Another means is required to establish the identity of the user on 
+            whose behalf a non-protocol engine thread executes through the JNDI
+            provider.
+        </para>
+        
+        <para>
+            JNDI inherently passes around parameters pertaining to its mode of
+            operation and to the identity of the user using environment 
+            parameters and properties.  Things like the user prinicipal and the
+            password of the user are inherited by one context which can in turn
+            lead to another.  In our particular situation calls on a Contexts 
+            and their subclasses result in calls by the server side JNDI 
+            provider to the nexus module.  These calls are made with respect to
+            a single context.  When calls are made on the nexus, the nexus
+            methods need to be able to access environment parameters without
+            passing around extra arguments.  Access to environment parameters
+            no matter how deep the calls into the nexus and its registered 
+            backends should occur on an as needed basis.  The nexus, backends
+            and other modules need to be able to differentiate between calls 
+            made by protocol module worker threads and other threads going 
+            through the JNDI provider bypassing the protocol.  
+        </para>
+        
+        <para>
+            In conclusion, every module used to conduct backend operations must 
+            first be able to differentiate between a protocol driven call and a
+            protocol bypassing call.  Secondly parameters required to conduct
+            operations must be extracted either through JNDI environment 
+            properties or through a ClientSession.  To do so threads must be 
+            tracked centrally somewhere and associated with these objects.  When
+            all operations complete or another context is used by the same 
+            thread the relationship is dismantled and another one is 
+            established.
+        </para>
+            
+        <para>
+            The ClientManager again is the perfect candidate for centrally 
+            managing thread to Context associations.  After all non-protocol 
+            driven calls through the JNDI provider are still server clients yet
+            not in the protocol sense as remote processes but in the method call
+            sense as local servers embedding LDAPd in the same process space.
+            Another reason why the ClientManager is perfect for this task is due
+            to the ability to referrence it from every module in the server.  
+            The ClientManager is the central master module.  Other modules 
+            implementing the ClientManagerSlave interface can get a handle on 
+            the ClientManager service without imposing cyclic dependencies 
+            within an Avalon Container like Phoenix.  It is the best place to 
+            keep data related to a JNDI contexts for referrence later on by 
+            modules requiring JNDI environment parameters without passing around
+            environments as arguments in each method call.
+        </para>
+        
+        <para>
+            An overload to the threadAssociate method taking a LdapContext as 
+            its only argument can be implemented to associate a thread with a 
+            JNDI LdapContext.  The context and its environment can then be 
+            referred at any time during the execution of method calls against 
+            the server's backend nexus.  The extraction of the user principal 
+            to set operational attributes simple requires a test to see if a 
+            JNDI LdapContext is associated with the calling thread.  If so the
+            environment of the context is access to get the user principal 
+            associated with the context and hence the executing thread.
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/listener-module.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/listener-module.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,172 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Listener Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.6 $</revnumber>
+                <date>$Date: 2003/05/03 01:45:23 $</date>
+                <revdescription>
+                    <para>
+$Log: listener-module.xml,v $
+Revision 1.6  2003/05/03 01:45:23  bearcej
+Refix image URLs
+
+Revision 1.5  2003/05/03 01:43:08  bearcej
+Add maven file to help generate server site.
+Modify graphic links again.
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.4  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.3  2003/04/08 02:29:55  bearcej
+* Clean up project.properties
+* Fix logos in project.xml
+* Fix links to image files in docs
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.3  2003/03/15 17:21:07  bearcej
+Fixed abstract section tag.  Fixed figure tags to conform to simple docbook
+Modified Files:
+ Tag: ALPHA-0_7
+    listener-module.xml
+
+Revision 1.1.2.2  2003/03/10 23:43:32  akarasulu
+Moved links so that image references point to /image/design.  Note that
+references do not have a [.] in front since the maven driven transforms
+automatically append [.] to references.
+
+Revision 1.1.2.1  2003/03/10 23:24:21  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.1  2003/03/09 22:14:08  akarasulu
+moved the listner.xml file to listener-module.xml
+
+Revision 1.1.2.4  2003/03/02 02:14:51  akarasulu
+Added sequence diagram for the listener thread loop.
+
+Revision 1.1.2.3  2003/03/02 00:37:31  akarasulu
+Adjusting spacing.
+
+Revision 1.1.2.2  2003/03/02 00:32:39  akarasulu
+Completed listener documentation but needs some work and added the article
+info tag to both these document.  Also flaged these docs as whitepapers.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+    <para>
+        The listener module is responsible for accepting client connections
+        on a tcp port of an ip interface.  In this sense it listens for 
+        client connections.  The module implements the ServerListener interface.
+    </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add links to other documents refered to
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+            Currently the ListenerModule only supports a single server socket
+            connection and does not presently support SSL.  It is a blocking
+            implementation requiring a blocked thread on the accept() method 
+            of the ServerSocket.
+        </para>
+
+        <para>
+            The module is not a stage yet it is not a simple module as defined
+            in the architecture document.  The module is a Runnable module that
+            uses a single thread to detect client connections on the server 
+            socket.  This thread runs until the Module is stopped.  The module
+            starts the listener thread in the start stage of the module.  The 
+            thread is created fresh and started - it is not claimed from a pool.
+        </para>
+        
+        <figure>
+            <title>Listener Thread Loop Sequence Diagram</title>
+            <graphic fileref="../images/ListenerModuleRun.gif"/>
+        </figure>
+
+        <para>
+            Once started the module's driving thread enters a while loop that
+            completes when the hasStarted() status returns false for the module.
+            A call to stop() will statisfy this stop condition.  Upon entering
+            the while loop the thread blocks on the ServerSocket's accept() 
+            method until a client connection is made.  accept() returns with a
+            client Socket which is packaged into a brand new ConnectEvent.  The
+            ConnectEvent is a special ClientManager specific event.  It is a 
+            SEDA event.  Once the ListenerModule creates the ConnectEvent with
+            the Socket inside, a call is made to the ClientManager service's
+            connectPerformed() method with the event as the sole argument.  Once
+            this call completes the thread starts the loop over again providing
+            the module has not been stopped.  It is very important to make sure
+            that ClientManager implementations do not spend too much time 
+            handling the event.  The ClientModule which implements the 
+            ClientManager service interface for this reason is implemented as a 
+            stage.  The idea here is that the cost of the connectPerformed 
+            event handling operation is merely translated into a SEDA stage 
+            enqueue.  The processing is handled later asynchronously by a 
+            ClientModule worker thread.  More details are available in the SEDA
+            Stage implementation document.  The use of a stage for the 
+            ClientModule makes connection handling instantaneous allowing the
+            listener thread to immediately return to listening on the port for
+            new clients.  The inability to quickly complete a loop upon an 
+            accept() return would translate into connection latencies when the 
+            client connection frequency is high.
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+            This module is very primitive and will see several changes as the
+            server matures and is optimized.  At this point we see obvious 
+            changes that merely add features.  At some point before a beta the
+            module will need to support listening for client connections on 
+            multiple ports and ip interfaces.  It must also enable the use of
+            SSL while handling the nuiances to accomodate the StartTLS extended 
+            request implementation.
+        </para>
+
+        <para>
+            Beta releases will optimize the server for various JDK platforms.  
+            There is very little that can be done to optimize the module for the
+            JDK1.3 and lesser platforms however this is not the case for JDK1.4.
+            By using selectable channels we can use a single thread to listen
+            for client connections on any number of ports and ip interfaces 
+            rather than allocating a thread for each tcp port.  This provides
+            better scalability for the server as the number of ports it listens 
+            to increase.  At the end of the day however just how many ports do
+            we expect an LDAP server to be listening to.  So optimization would
+            almost be moot.  Regardless if a commital to optimizing the module
+            is made for the JDK1.4 then a JDK based swapping schema must be 
+            used to switch the implementation.  To have a single code base run
+            on both the JDK1.4 and be compatible for lesser versions, we must
+            swap selectable new IO channels for simple io at server 
+            initialization time when the JDK version can be queried from the
+            system properties.
+        </para>
+    </section>
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/module-implementation-template.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/module-implementation-template.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.3 $</revnumber>
+                <date>$Date: 2003/05/03 01:21:32 $</date>
+                <revdescription>
+                    <para>
+$Log: module-implementation-template.xml,v $
+Revision 1.3  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.1  2003/03/10 23:24:21  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.1  2003/03/03 04:49:25  akarasulu
+Added as placeholders for now.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+        </para>
+
+        <para>
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/nexus-module.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/nexus-module.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,298 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.6 $</revnumber>
+                <date>$Date: 2003/05/03 01:45:23 $</date>
+                <revdescription>
+                    <para>
+$Log: nexus-module.xml,v $
+Revision 1.6  2003/05/03 01:45:23  bearcej
+Refix image URLs
+
+Revision 1.5  2003/05/03 01:43:08  bearcej
+Add maven file to help generate server site.
+Modify graphic links again.
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.4  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.3  2003/04/08 02:29:55  bearcej
+* Clean up project.properties
+* Fix logos in project.xml
+* Fix links to image files in docs
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.3  2003/03/15 17:19:44  bearcej
+Fixed figure tags to confirm to simple docbook.
+Modified Files:
+ Tag: ALPHA-0_7
+    nexus-module.xml
+
+Revision 1.1.2.2  2003/03/10 23:43:32  akarasulu
+Moved links so that image references point to /image/design.  Note that
+references do not have a [.] in front since the maven driven transforms
+automatically append [.] to references.
+
+Revision 1.1.2.1  2003/03/10 23:24:21  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.2  2003/03/10 21:44:14  akarasulu
+Done with nexus module documentation
+
+Revision 1.1.2.1  2003/03/03 04:49:25  akarasulu
+Added as placeholders for now.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+            The NexusModule implements the UnifiedBackend service and was 
+            intended as a Backend hub or nexus for glueing together multiple 
+            naming contexts into one unified system while centralizing the 
+            management of operational attributes.  The module routes Create,
+            Read, Update and Delete (CRUD) method calls on entries as well
+            as Directory Information Base (DIB) management operations to owning 
+            Backends.  As a wrapper around all server Backends it has the unique
+            opportunity to centralize the maintanance of operational attributes 
+            for all entries before and after entry CRUD method calls and DIB 
+            management operations.
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+
+
+    <section>
+        <title>Architecture</title>
+        
+        <para>
+            Breifly stated, the architectural goal is to have one central 
+            naming context independent service responsible for all Directory 
+            Information Base (DIB) operations.  Centralized DIB managment 
+            enables the transparent introduction of operational attributes in 
+            a standard manner independent of context assigned Backends.  Backend 
+            implementations need not manage operational attributes other than 
+            those specific to and required by their implementations.  Backend
+            implementation operational attribute maintenance overheads, 
+            complexity and coupling to other server modules decrease when a
+            centralized unified backend nexus service is introduced.
+        </para>
+        
+        <para>
+            The org.apache.eve.backend package within the ldapd-server subproject 
+            defines three critical interfaces to enable the aforementioned 
+            architectural goals.  The core Backend interface declares the 
+            signatures of entry CRUD operations as well as other DIB management
+            operations.  The interface does not define a ROLE constant.  The
+            primary purpose of the Backend interface is structural: 
+            implementations of this interface are not expected.  The lack of a 
+            ROLE constant revieals our intention: the Backend interface alone 
+            is insufficient for a viable backend service.  See Figure I below:
+        </para>
+        
+        <figure>
+            <title>Figure I: Backend Interfaces</title>
+            <graphic fileref="../images/BackendInterfaces.gif"/>
+        </figure>
+        
+        <para>
+            Two service interfaces are defined with a ROLE constant member: 
+            UnifiedBackend and AtomicBackend.  Implementations of these 
+            interfaces would constitute a viable service.  Both service 
+            interfaces extend from the Backend base interface.  Extention from
+            Backend implies the ability of both sub interfaces to handle entry
+            CRUD operations and DIB management operations.  These relationships
+            are depicted in the UML diagram above.
+        </para>
+            
+        <para>
+            The AtomicBackend service interface declares additional methods
+            required by concrete backends assigned as the backing store to one
+            and only one naming context.  Context specificity is implied by 
+            the getSuffix() method declaration in AtomicBackend.  Extention of
+            the BackendConfig interface by the AtomicBackend further 
+            differntiates it from the UnifiedBackend by introducing backend 
+            configuration parameter management interfaces.  As its name 
+            suggests, the AtomicBackend is clearly designed to implement an
+            indivisible backend for a single naming context specified by a
+            distinguished name suffix.  For more information see the <ulink 
+            url="backend-module.html">BackendModule</ulink> documentation.
+        </para>
+            
+        <para>
+            The UnifiedBackend service interface contains methods that 
+            differentiates its role from the AtomicBackend interface.  These
+            methods lookup, register, unregister and list AtomicBackends and 
+            the suffixes of their naming contexts.  The registration methods
+            of the interface allow the addition and removal of AtomicBackends
+            from the nexus service.  The service enables lookups of currently 
+            registered AtomicBackends through the getBackend() method which 
+            looksup AtomicBackends by suffix distinuished names.  Registered 
+            AtomicBackends or their suffixes are listed using the listBackends()
+            and listSuffixes() methods.  The interface also contains naming 
+            context independent methods.  An example would be the getRootDSE() 
+            method, which returns the Root Directory Server Agent (DSA) Entry 
+            or RootDSE.  The RootDSE resides at a nameless context and is looked 
+            up using a one level search with an empty search base distinguished 
+            name.  The unified backend is the best location for context indepent
+            entries like the RootDSE.
+        </para>
+        
+        <para>
+            Other utility methods on the UnifiedBackend provide a convenient
+            means to parse distinguished names as Strings into javax.naming.Name
+            object instances or to get a valid parser for a specific DN.  These
+            methods help present a facade to schema driven distinguished name
+            parsering.  They remove the need for modules upstream of the nexus
+            to have to interface with the SchemaManager.  The nexus hence acts
+            like a one stop service for modules like the ProtocolModule.
+        </para>
+    </section>
+    
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+            The NexusModule implements the UnifiedBackend interface and extends
+            the AbstractModule base class.  The Backend interface method 
+            implementations merely wrap calls to underlying AtomicBackends after 
+            resolving a distinguished name to the owning AtomicBackend.  All 
+            Backend methods provide some manner of access to a distinguished 
+            name which localizes the operation within a specific naming context.
+            The distinguished name is either provided as an argument or can be
+            accessed from an argument passed into the method.  If the resolution
+            of a distinguished name to a registered AtomicBackend fails, the 
+            method throws the appropriate subtype of NamingException.  For these
+            reasons and others virtually all Backend interface methods throw the
+            NamingException superclass.
+        </para>
+        
+        <para>
+            Perhaps the most significant method implementations for the module
+            are those that resolve a distinguished name to a naming context, 
+            enable search traps for returning the RootDSE and configure the
+            module.  Each of these method implementations are described in the
+            paragraphs below.
+        </para>
+        
+        <para>
+            The getBackend() method resolves a distinguished name provided as a
+            javax.naming.Name argument into a naming context and returns the
+            AtomicBackend associated with the context.  The NexusModule keeps a 
+            Map of suffix distinguished names mapped as java.lang.Sting keys to 
+            AtomicBackend instances.  To lookup and return the AtomicBackend the
+            backend suffix must be extracted from Name argument.  First before
+            considerable investment, the method checks to see if the argument 
+            represents a suffix.  Next it presumes the Name argument represents
+            a non-suffix entry and cycles through all contexts looking for the
+            one whose suffix is a prefix to the Name argument.  If the loop
+            does not find a valid AtomicBackend, an IllegalArgumentException
+            is thrown.  Otherwise the AtomicBackend is returned.
+        </para>
+        
+        <para>
+            Without traping search requests, a RootDSE search would result in
+            an IllegalArgumentException in getBackend() due to the empty String
+            provided as the search base.  Remember to list the contents of the 
+            RootDSE the search base must be set to the empty string.  The nexus
+            inspects every search request to see if it needs to bypass normal
+            search processing.  Normally the nexus would forward the search 
+            request to the appropriate backend resolved using the search base.
+            Upon detection of a search with base scope and an empty string for
+            the search base, the nexus bypasses normal search processing.  It
+            creates a special Cursor which returns only one entry: the RootDSE.
+        </para>
+        
+        <para>
+            The configuration of the module simply extracts the attributes of
+            the RootDSE from the config.xml.  The attributes are stored in XML
+            using &lt;attribute&gt; tags having two manditory attributes: "name"
+            and "value".  The name attribute is used as the id of the LDAP 
+            attribute to add to the RootDSE.  The value attribute is obviously
+            used as the value of the named LDAP attribute.  These attributes are
+            used to build the RootDSE entry on the spot.  The RootDSE is a
+            special implementation of LdapEntry specifically for this purpose.
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+            Enable automatic updates to the RootDSE whenever new naming contexts
+            are created or destroyed with the registration and unregistration of
+            AtomicBackends.  The namingContexts attributes would be added and 
+            removed when Backends are registered and unregistered respectively.
+        </para>
+
+        <para>
+            Enable administrative naming contexts for cn=schema and other 
+            administrative areas.
+        </para>
+        
+        <para>
+            Start managing critical operational attributes defined in RFC2251
+            for the creators name, creation timestamp or the modifiers name etc.
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+            Does not manage operational attributes.
+        </para>
+        
+        <para>
+            We need to reinvestigate the service interfaces implemented by this
+            module as well as AtomicBackends to find a better more JNDI like
+            mechanism for operating against a DIB.  These new interfaces should 
+            make the JNDI provider implementation trivial if designed correctly.
+            Currently, serveral transformations of an LdapEntry to an Attributes
+            object in the JNDI provider impose inefficiencies and extra code in
+            the provider.  By reducing the rift between the Backend super 
+            interface and the JNDI API methods these inefficiencies can be 
+            avoided while reducing the code base.  As a pleasent side effect,
+            upstream dependent modules can use the JNDI provider rather than 
+            access the nexus directly.  This would make those dependant modules
+            easier to understand since JNDI APIs are standard JDK APIs more 
+            familiar to most developers than the Backend, AtomicBackend, and
+            UnifiedBackend interfaces as they exist today.  Another major 
+            benefit gained from the use of the internal server side JNDI 
+            provider would be entry location transparency.  If JNDI is used 
+            references can be automatically resolved by the provider based on
+            the controls in effect.  The protocol module would not have to
+            directly invoke the Sun LDAP JNDI provider to resolve references on 
+            behalf of the user.  The server-side provider would handle 
+            references in a manner consistant with the controls bound to the 
+            ClientSession.  The protocol module would just use JNDI would little 
+            regard concerning the nature of the entries returned.  The use of 
+            the server-side JNDI provider would hence greatly simplify the 
+            implementation of the ProtocolEngine.
+        </para>
+    </section>
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/operations/opertion-template.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/operations/opertion-template.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,106 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>LDAPv3 Protocol Operation Design And Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.2 $</revnumber>
+                <date>$Date: 2003/05/03 01:21:33 $</date>
+                <revdescription>
+                    <para>
+$Log: opertion-template.xml,v $
+Revision 1.2  2003/05/03 01:21:33  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.1  2003/03/28 05:12:35  akarasulu
+Adding documents to track changes to protocol enhancements.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Comments on Protocol Requirements</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+    <section>
+        <title>Design</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+        </para>
+
+        <para>
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/operations/search-operation.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/operations/search-operation.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,745 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>LDAPv3 Search Operation Design And Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.7 $</revnumber>
+                <date>$Date: 2003/05/03 01:21:33 $</date>
+                <revdescription>
+                    <para>
+$Log: search-operation.xml,v $
+Revision 1.7  2003/05/03 01:21:33  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.6  2003/04/04 01:40:16  akarasulu
+Added more to search operation document yet temporarily suspended it to
+start working on request scope management design which all operations will
+eventually depend upon.
+
+Revision 1.5  2003/04/02 22:15:00  akarasulu
+Added section on jdbm backend design for alias dereferencing.
+
+Revision 1.4  2003/04/02 16:23:39  akarasulu
+Exploring issues dealing with alias cycles, indirect alias chains and how
+they must be handled.
+
+Revision 1.3  2003/04/02 02:15:24  akarasulu
+Got down to how we should finally handle alias dereferencing modes.
+
+Revision 1.2  2003/03/31 13:14:12  akarasulu
+Added more documentation on alias dereferencing.
+
+Revision 1.1  2003/03/28 05:12:35  akarasulu
+Adding documents to track changes to protocol enhancements.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Comments on Protocol Requirements</title>
+
+        <section>
+            <title>Aliases and Search Operations</title>
+            
+            <para>
+                Alias entries according to X.501 are entries "of the class 
+                'alias' containing information used to provide an alternative 
+                name for an object."  The objectClass definition for an alias
+                is provided below along with the attributeType definition for
+                its sole required attribute aliasedObjectName:
+            </para>
+            
+            <programlisting>
+                objectclass ( 2.5.6.1 NAME 'alias' SUP top STRUCTURAL
+                    MUST aliasedObjectName )
+            
+                attributetype ( 2.5.4.1 NAME 'aliasedObjectName'
+                    EQUALITY distinguishedNameMatch
+                    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )
+            </programlisting>
+            
+            <para>
+                The aliasedObjectName is uses the distinguishedNameSyntax.
+                Aliases can only refer to entries local to the DSA since they
+                only represent distinguished names and not an LDAP URL.  There 
+                seems to be no documentation pertaining to the use of aliases 
+                that span across DIBs.  Hence we presume an alias in one backend
+                can refer to an entry within another.  Aliases cannot refer to 
+                other aliases in an alias chain according to X.501.  Write 
+                operations are not allowed to affect the DIT through aliases 
+                (need confirmation about this).  There seems to be a defunct 
+                IETF draft that never made it to RFC which discusses the 
+                expected behavoir of aliases.  See <ulink url=
+"http://www.globecom.net/ietf/draft/draft-byrne-ldap-alias-00.html">
+                draft-byrne-ldap-alias-00 </ulink> for more information 
+                regarding alias behavior in LDAPv3.  While reading this draft
+                note that it often violates RFC 2251 - namely by allowing 
+                aliases to other aliases in a chain.
+            </para>
+            
+            <para>
+                According to X.511 section 12.5.2.1 the following error codes
+                are associated with aliases (see <ulink url=
+"http://www.alternic.org/drafts/drafts-j-k/draft-just-ldapv3-rescodes-02.txt">
+                Error Code Definitons Draft</ulink> for detailed error code
+                explanations):
+            </para>
+            
+            <table frame='all'><title>Mandarory LDAPd Services</title>
+                <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+                    <colspec colname='ErrorName'/>
+                    <colspec colname='LDAPv3Code'/>
+                    <colspec colname='Description'/>
+                    <thead>
+                        <row>
+                            <entry>Error Name</entry>
+                            <entry>LDAPv3 Code</entry>
+                            <entry>Error Condition Description</entry>
+                        </row>
+                    </thead>
+                    <tbody>
+                        <row>
+                            <entry>aliasProblem</entry>
+                            <entry>33</entry>
+                            <entry>An alias has been dereferenced which names 
+                                no object - a broken link where destination 
+                                entry does not exist. [X511, Section 12.5].
+                            </entry>
+                        </row>
+                        <row>
+                            <entry>aliasDereferencingProblem</entry>
+                            <entry>36</entry>
+                            <entry>An alias was encountered in a situation where
+                                it was not allowed.  For example an add that 
+                                creates an alias to another alias could throw 
+                                this error or a delete operation going through
+                                an alias.  If the client does not have read 
+                                permission for the aliasedObjectName attribute 
+                                and its value then the error should be returned.
+                                [X511, Section 7.11.1.1]
+                            </entry>
+                        </row>
+                    </tbody>
+                </tgroup>
+            </table>
+            
+            <para>
+                According to section 4.5.1 of RFC 
+                <ulink url="http://www.faqs.org/rfcs/rfc2251.html">2251</ulink>,
+                search requests contain a search operation modifier called
+                derefAliases which affect the manner in which aliases are 
+                handled during the course of a search operation.  Parts of the 
+                RFC's section defining the values of the derefAliases search 
+                parameter are listed in the block below:
+            </para>
+            
+            <programlisting>
+
+            Search Request ASN.1 Notation Snippet:
+            --------------------------------------
+            
+                derefAliases    ENUMERATED {
+                        neverDerefAliases       (0),
+                        derefInSearching        (1),
+                        derefFindingBaseObj     (2),
+                        derefAlways             (3) }
+            
+            Indicator Commentary Snippet:
+            -----------------------------
+            
+            derefAliases: An indicator as to how alias objects (as defined in
+            X.501) are to be handled in searching.  The semantics of the
+            possible values of this field are:
+        
+                    neverDerefAliases: do not dereference aliases in searching
+                    or in locating the base object of the search;
+        
+                    derefInSearching: dereference aliases in subordinates of
+                    the base object in searching, but not in locating the
+                    base object of the search;
+        
+                    derefFindingBaseObj: dereference aliases in locating
+                    the base object of the search, but not when searching
+                    subordinates of the base object;
+        
+                    derefAlways: dereference aliases both in searching and in
+                    locating the base object of the search.
+                     
+            </programlisting>
+            
+            <para>
+                The definitions for the four modes of alias handling raise 
+                questions regarding alias handling behavoir.  These questions
+                deal with the order in which dereferencing and search selection 
+                criteria are applied.  If alias dereferencing is applied before
+                the application of search criteria then the filter takes affect
+                on the target entry referred to by the alias.  Conversely if
+                dereferencing is applied after the application of search 
+                criteria then the filter is applied to the alias itself and not 
+                on the target entry.  In the later case the alias may never be 
+                a candidate for dereferencing if the search filter does not 
+                select it.  Although a subtle nuance in alias handling, the 
+                order of handling can produce dramatic differences in the 
+                returned search result set.
+            </para>
+            
+            <para>
+                These questions must be answered before choosing a design 
+                approach that incorporates the handling of aliases.  The 
+                existing LDAP and DAP standards do not explicitly confront these
+                crutial details.  Perhaps the lack of standardization is the
+                reason why so many directory servers do not support aliases.
+                The SUN One Directory Server for example does not support 
+                aliases at all.  Although a sticky point aliases provide 
+                an immeasurable degree of flexibility, and should be supported.
+                Ultimately we need to pose these questions to both the LDAP and
+                DAP communities to find the appropriate answers.  For the time 
+                being some inferences can be drawn from what little information 
+                we do have regarding alias handling.  These inferences can be 
+                presented to the community for definitive answers after we draw
+                them out below.
+            </para>
+            
+            <para>
+                Let's consider alias handling for searches when the search base 
+                is an alias.  In neverDerefAliases or in derefInSearching modes 
+                the search base is never dereferenced.  Hence it must be treated
+                like any other regular leaf entry.  Depending on the search 
+                scope and the filter used there are two possible outcomes: 
+                either nothing is returned or the search base entry (the alias)
+                is returned.  Regardless of the filter used, a search scope set 
+                to one level search will always return nothing.  One level 
+                scoped searches never return the search base entry in the result
+                set.  If base level or subtree level search scope is used, then 
+                the alias is returned in the result set only if the search 
+                filter selects the base (alias) entry.  For example a filter of
+                '(objectClass=alias)' will select the alias entry when the 
+                search scope is set to the base or subtree level scope.  If the
+                alias entry is to be returned it is packaged in a 
+                SearchResultEntry PDU and sent to the client.  A final 
+                SearchResultDone PDU signals the completion of the search 
+                operation.  If the alias is not selected a single 
+                SearchResultDone PDU only.
+            </para>
+                
+            <para>    
+                If a search request has derefAliases set to derefAlways or 
+                derefFindingBaseObj then the search base if it is an alias is 
+                dereferenced before the search filter is applied.  The effective
+                search base then becomes the DN of the entry pointed to by the 
+                alias.  In these cases alias, dereferencing of the base must be 
+                applied before the search begins.  Use of the aliased DN 
+                propagates it to the new search base.  Ironically the previous 
+                filter example '(objectClass=alias)' using the base search scope 
+                should return nothing in these modes which do dereference the
+                base entry.  The reason being is the dereferenced base cannot 
+                be an alias since chaining is not allowed.  So once the new base
+                is dereferenced, and the filter is applied, the filter 
+                expression will not select it.  Based on the position of the 
+                new search base, the search scope, and the search filter the 
+                returned result set could be just about anything.  We can only 
+                comment on this one special filter example's result.
+            </para>
+                
+            <para>
+                Search behavoir under these cases enabling search base 
+                dereferencing are well understood and documented.  It is clear
+                in these cases that dereferencing of aliases must occur before
+                the application of search filters to select dereferenced entries
+                and their descendants.  Standards however lack clarity with 
+                regard to search behavoir when alias dereferencing is totally 
+                enabled while searching.  To obtain a solid footing we have 
+                decided to take a leap of faith with a simple presumption 
+                regarding alias handling consistancy.  The presumption stems 
+                from our clear conclusions regarding the order of dereferencing
+                found for alias handling on search bases.  In this light, we 
+                presume the consistant application of alias dereferencing 
+                before the application of search criteria across all alias 
+                handling modes.  So with this premis, derefInSearching mode on 
+                a subtree scoped search would dereference every descendant 
+                alias under the search base whether or not the search filter 
+                selects descendant alias entries.  The impact of this premis 
+                aligns perfectly well with search semantics and the concept of 
+                name aliases.  Aliases alter both the DIT structure and the 
+                namespace by providing alternative names for entries.  In 
+                affect, under the various modes of alias handling they present 
+                different views of the DIT to search operations.  When aliases 
+                are never dereferenced the DIT remains a perfect tree structure 
+                with aliases represented as leaf entries.  Without 
+                dereferencing, aliases have no influence on the effective 
+                namespace presented to a search operation.  When alias
+                dereferencing is totally enabled, the effective DIT structure 
+                and namespace, which a search filter operates on, differs from
+                modes where alias dereferencing is totally disabled.  The 
+                alternative view of the DIT structure and namespace represents 
+                aliased names and alias induced structural relationships as 
+                actual entry names and relationships as if aliases never 
+                existed in the first place.  The new modifed view of the DIT, 
+                depending on the aliases used, may be a tree or it may be a
+                graph.  Regardless the search algorthim which applies the filter
+                to the DIB, should operate on the modified view when alias 
+                dereferencing has been totally enabled.
+            </para>
+                
+            <para>
+                In conclusion, all alias handling modes, if at all, must 
+                dereference aliases before applying search filters to select
+                DIT entries.  This clarifies our approach in dealing with the
+                two modes: derefInSearching and derefAlways.  derefAlways after 
+                the initial search base is dereferenced reduces to the 
+                derefInSearching mode by using the DN of entry referenced by the
+                alias.  After this step the handling of these modes are the 
+                same.  To conduct searches in derefAlways and derefInSearching 
+                modes the alias dereferencing mechanism is unfortunately 
+                optimally implemented within a backend module's search algorthm 
+                to enable dereferencing before the application of search 
+                criteria.  This is unfortunate because it demands the knowledge
+                of alias handling behavoir on the part of backends and their 
+                implementors.
+            </para>
+            
+            <para>
+                Another potential pitfall while dereferencing aliases during
+                searches with subtree scope appears when aliases induce cycles
+                within the DIT.  Cycles cause infinite loops or the return
+                of redundant entry copies to the client.  A simple cycle
+                results when an alias refers back to a relative within the 
+                heirarchy.  Dereferencing the alias to propagate the search back
+                at the relative eventually encounters the alias once again and
+                so on in an infinite search loop.  One approach to mitigate the
+                problem would be to restrict alias creation, by rejecting the
+                addition of aliases to the DIT which cause cycles where the
+                offending alias refers back to an immediate or distant parent.
+                Another approach would be to track search propagation preventing
+                the dereferencing of aliases more than once.  Although the 
+                approach prevents an infinite loop it does not prevent the
+                possibility of returning duplicate entry copies to the client.
+                The search would have to track the entries it has already 
+                returned to prevent the redundant return of duplicates.
+            </para>
+            
+            <para>
+                More complex situations occur where indirect alias chains cause 
+                cycles.  When refering to indirect chains we do not mean the 
+                referral of one alias to another.  This is not allowed to begin
+                with.  Indirect chains occur when one alias refers to an entry,
+                which has a descendant alias that refers back to a parent of the
+                first alias.  Under a subtree scoped search with dereferencing 
+                enabled, the search would eventually cycle from the first alias
+                to the second and back to the first.  This "indirect chain" 
+                presents the same challanges encountered in a simple cyclic
+                alias.  The chain however is much harder to detect and its 
+                affects are less trivial to handle than the simple cycle 
+                inducing alias.  These problems must be confronted regardless
+                of the design approach.
+            </para>
+            
+            <para>
+                With respect to JNDI, alias dereferencing is controled using the
+                "java.naming.ldap.derefAliases" environment property.  The 
+                "dereference links" flag obtained via the getDerefLinkFlag() on
+                SearchControls has nothing to do with alias dereferencing.  
+                This is clearly stated in the tutorial for JNDI.  However, the
+                flag's purpose is unknown due to lack of documentation by SUN.  
+                Can someone find out what it's used for?  Also when resolving 
+                the name of an aliased entry using the NameClassPair method 
+                getName() on dereferenced aliases, the name returned will be the 
+                full distinguished name of the aliased entry and not the 
+                relative distinguished name.  As a consequence the isRelative()
+                method for the NameClassPair should return false.  This nuance 
+                yet a requirement of JNDI must be meet by the server side JNDI 
+                provider once alias dereferencing is enabled.
+            </para>
+        </section>
+    </section>
+    
+    <section>
+        <title>Design</title>
+        
+        <section>
+            <title>Alias Handling in Search Operations</title>
+            
+            <para>
+                With some of our conclusions in the protocol commentary section
+                above regarding alias handling, we now consider some design 
+                approaches in this section.  These approaches are dependent on 
+                the existing server design and so we breifly recap its relavent
+                aspects.
+            </para>
+            
+            <para>
+                The search operation is conducted within the ProtocolModule
+                using a search request processor.  The processor extracts search
+                parameters from the SearchRequest PDU and calls the search 
+                method on the nexus to return a Cursor over the result set.  The
+                processor then enters a loop to pull entries one at a time from 
+                the backend using the search Cursor.  Each iteration transmits
+                an entry packaged within a SearchResultEntry PDU to the client.
+                The Cursor returned by the search method of the nexus, is 
+                actually created by calling the search method on the backend 
+                associated with the naming context of the search base.  In this 
+                respect the nexus design presumes the confinement of a search 
+                operation to a single backend.  The current nexus design 
+                presents challanges for alias dereferencing when aliases in one
+                backend refer to entries in other backends.  "DIB crossing 
+                aliases", as we coin them, may require a completely different 
+                approach to handling dereferencing.
+            </para>
+                
+            <para>
+                With the design in mind we now explore the design options
+                for the various alias dereferencing modes.  Without alias
+                dereferencing support the server effectively operates in
+                neverDerefAliases mode.  The alias is returned itself within a
+                SearchResultEntry PDU as if it is a terminal leaf entry.
+                derefFindingBaseObj mode could be handled trivially before
+                calling search on the nexus within the search processor.  Search
+                base dereferencing can also be handled within the nexus itself.
+                In these cases, the search base entry could be checked to see
+                if it is an alias.  If it is an alias the target entry it points
+                to could be dereferenced and used as the base of the search when
+                the call to the nexus or owning backend is made.  The amount of
+                change required to enable this mode is trivial with several
+                implementation alternatives.
+            </para>
+            
+            <para>
+                The other two modes, derefInSearching and derefAlways, would
+                require non-trivial changes to handle alias dereferencing since
+                they pervade into the searching mechanism.  Requirin the
+                dereferencing of aliases before the application of search
+                selection criteria, tightly couples dereferencing with the
+                search algorithm.  In an effort to simplify our approach, we
+                reduce the two modes into one.  As mentioned before the only
+                difference between derefInSearching and derefAlways modes is
+                the fact that the search base if it is an alias is resolved
+                before the search.  So for all practical purposes we can reduce
+                the derefAlways mode into the derefInSearching mode by
+                dereferencing the search base to the aliased DN.  Then the
+                dereferenced entry can then be used as the search base while
+                conducting the search as if it were in derefInSearching mode.
+                Even with this reduction, derefInSearching mode is not a simple
+                mode to manage especially with whole subtree scoped searches.
+                Many approaches can be taken for dereferencing aliases when in
+                derefInSearching mode.  We shall explore the options within the
+                rest of this section to determine the most optimal design for
+                searches conducted in derefInSearching mode.  All options below
+                are geared specifically to the toughest case to handle: a
+                search in derefInSearching mode with the scope set to the whole
+                subtree.
+            </para>
+            
+            <section>
+            <title>Alias Handling Outside of Backends</title>
+            
+            <para>
+                Already congested Backends can be spared by embedding alias 
+                dereferencing fuctionality into the protocol engine's search 
+                request processor or into the nexus module.  While iterating 
+                through the resultant cursor the search processor may encounter 
+                aliased entries.  On an alias entry the processor can conduct a 
+                subordinate search using the DN of the aliased entry as the 
+                search base with the parent search's parameters.  Once a 
+                subordinate search cursor is consumed the parent search can 
+                carry on from where it left off.
+            </para>
+            
+            <para>
+                This approach has several problems.  The the most apparent lies
+                in the fact that a subordinate search may return yet another
+                alias and its subordinates may in turn return more aliases and 
+                so on.  A recursive solution would be required to resolve the 
+                possible heirarchy of alias triggered subordinate searches.  A 
+                special cursor can be designed to encapsulate the process and 
+                lessen the amount of impact this would produce on the regions 
+                responsible for alias dereferencing.  At some point during the 
+                search the number of subordinate search cursors waiting for the 
+                last cursor in the chain to complete will be equal to the depth
+                of the deepest alias chain in the search.  That is if there are 
+                no time or size limitations.  Furthermore there is the danger 
+                of cyclic aliases mentioned before.  To prevent such situations
+                the search must maintain a common cache of entries that it has 
+                already returned.  The cache must be shared by all subordinate 
+                search cursors.  Alias dereferencing functionality implemented 
+                outside of the backend need not be pushed into the processor - 
+                it could reside in the nexus (a.k.a. the UnifiedBackend).  
+                Regardless of where it is located, if it does not reside within 
+                backends, it will have the same set of challanges to confront.  
+                These challanges will require recursive solutions.
+            </para>
+            
+            <para>
+                This approach is certainly not trivial however it does spare 
+                backends from having to manage aliases.  If aliases were 
+                managed by backends then they would have to manage them for all
+                protocol operations.  Each backend would have to apply its own
+                unique means to accomplish this depending on the nature of its 
+                backing store and hence its own design.  We explore these issues
+                with regard to the JDBM backend in the section to follow.
+            </para>
+            </section>
+            
+            
+            <section>
+            <title>Adding Alias Dereferencing Functionality To Backends</title>
+            <para>
+                The only other alternative design would be to delegate alias
+                dereferencing to backends.  This as mentioned before further 
+                complicates backend implementations making life even more 
+                difficult for backend implementors.  It also limits aliases,
+                preventing their use across DIBs.  Alias dereferencing design
+                and implementation would be highly specific to the nature of the
+                backend's backing store and would have to be implemented for 
+                each backend.  When analyzing the possibilities of this option 
+                we would have to focus on a specific backend, which makes the 
+                exercise valid for only that backend.  Nevertheless the 
+                exercise will lead to a better understanding of the hurdles to
+                overcome within backends in general.  For the exercise we chose
+                the default Jdbm based backend or modjdbm.  See the module 
+                documentation for more details on its operation.  We will 
+                presume the reader is already familiar with its design and its 
+                implementation.
+            </para>
+            
+            <para>
+                The modjdbm search algorithm factors scope directly into a
+                modified search filter's tree using a special type of node 
+                called a ScopeNode.  The search engine creates a new tree with a 
+                conjuction node at the root.  It attaches the ScopeNode 
+                underneath the root along with the filter expression tree 
+                generated from the user supplied search filter.  This is done 
+                before the optimization stage where the tree is evalutated by 
+                the search optimizer.  Introduction of the scope node before 
+                optimization considers scope while deciding the optimal search
+                route.  If the ScopeNode is chosen as the iteration loop a 
+                Cursor is created over entry ids which comply with search scope
+                requirements based on the search base.  For subtree scope the
+                ids of all descendants of the search base and the base entry 
+                itself are returned.  The cursor operates by performing a full
+                index scan on the DN index testing each entry to see if it is 
+                accepted by a descendant Assertion.  Entry DN's accepted by the 
+                assertion have their ids returned.  The optimizer rarely selects
+                the ScopeNode for iteration in subtree scope, due to an 
+                inefficient full index scan.  The search algorthim in most cases
+                opts to use the ScopeNode in assertion checks rather than in
+                iteration loops.  Whether the ScopeNode is used in iteration or
+                for assertion checks, the same Assertion is used to validate 
+                entries to return.  While iterating the candidate is accepted by
+                the assertion and in a lookup the candidate is also accepted or
+                rejected by the assertion.
+            </para>
+            
+            <para>
+                At the present moment the assertion used for the ScopeNode of
+                a subtree search leverages a single DN to determine if a 
+                candidate is a descendant.  In derefInSearching mode, all 
+                aliases and any indirect chains can be detected before the 
+                search begins.  The DNs of these aliases can be used to build
+                a composite disjunction Assertion for the ScopeNode used in 
+                iterations and lookups.  When used in iteration, the Cursor 
+                built for the ScopeNode equiped with the disjunction Assertion,
+                will return candidates under and at all the alias DNs.  In 
+                lookups, the disjunction Assertion will accept the ids of 
+                entries that are aliased as well as their descendants.
+            </para>
+            
+            <para>
+                The composite descendant Assertion built on multiple DNs can be 
+                spared the trouble of testing each candidate against all aliased
+                DNs.  Aliased entry DNs and the base DN can be consolidated 
+                depending on the relationships between the search base and the
+                aliased entries.  If an aliased entry is a descendant of the 
+                search base then it need not be added to the Assertion.  The 
+                subtree search on the base will eventually included the aliased
+                DN entry and its descendants.  Likewise the base DN need not
+                be added if an aliased entry refers to a parent of the DN.  The
+                subtree search from the aliased parent will eventually include
+                the base entry and its descendants.   This technique can be used
+                to consolidate M related DNs into one DN which is the top most 
+                parent of all descendant DNs.  DN consolidation makes simple 
+                cycles irrelavant to the search without dealing with redundant
+                returns or infinite loops.
+            </para>
+            
+            <para>
+                The one detail left out of all is the mechanism for detecting
+                the set of aliases dereferenced in the search and making their 
+                aliased entry DNs available to the search engine.  A special 
+                index can be built to track aliased names unlike the way normal 
+                indices work.  This special index would map the DN of the alias 
+                to the DN of the entry the alias refers to.  The search engine 
+                would start by scanning the keys of the index looking for DNs 
+                that are descendants of the search base.  The descendant DNs 
+                represent the DNs of all the aliases existing under the search
+                base.  The values of these DNs are appended to the set of DNs
+                that we need to add to the ScopeNode Assertion.  After the 
+                initial scan a similar scan is performed for every value added 
+                to the set until all values added have been checked to see if 
+                they have descendant aliases.  This way we capture all DNs
+                involved in alias chains.  In effect, alias dereferencing is 
+                being performed here for all aliases before the search begins.
+                Once all possible aliased entry DNs are resolved and accumulated
+                within a set, the consolidation process can begin.  The 
+                remaining DNs after consolidation are used to build the subtree
+                ScopeNode's Assertion.  Search then continues in the same 
+                fashion as it has before.  Surprisingly the solution is clean
+                and can be encapsulated outside of the search mechanism.
+            </para>
+            
+            <para>
+                The modjdbm design for alias dereferencing works out 
+                surprisingly well.  The process of dereferencing and the 
+                application of filter selection criteria are more seperable 
+                than we had first imagined.  The performance degradation due to
+                alias dereferencing is nominal.  The issues described within 
+                the "Future Considerations" section to follow do not hold with 
+                such a design - server side sorts can still be implemented 
+                without elaborate designs.  The only drawback is the inability 
+                to implement DIB crossing aliases.  Situations requiring such 
+                constructs can rely on the use of referrals instead.  These
+                surprising conclusions lead us to believe that alias handling
+                is best left upto the individual backend.
+            </para>
+            </section>
+            
+            <section>
+            <title>Furture Considerations</title>
+            <para>
+                Another consideration for choosing between these approaches is
+                the affect it will have on the ease in which other aspects of 
+                search will be designed and implemented.  Namely we are 
+                concerned with support for search operational modifiers like
+                control extentions.  An example of a popular search control 
+                extention is the server side sorting control.  This control
+                enables clients with a small memory foot print which cannot 
+                possibly hold the entire result set for sorting search results
+                by delegating the sort to the server.  The entries are returned 
+                sorted based on an attribute specified by the control.  
+            </para>
+            
+            <para>
+                The most efficient implementation for server side sorting would
+                leverage the sorted indicies of backends to have them return 
+                entries in sorted order.  The alternative of caching the result
+                set on the server after a search to sort the result before 
+                returning it would consume too much memory and processing time.
+                Backends could be asked to leverage their specific indexing 
+                scheme to return entries already sorted since indices usualy 
+                maintain their keys in sorted order.  It would be a waste to 
+                not take advantage of this, only to spend time and memory to
+                sort the results afterwords.
+            </para>
+            
+            <para>
+                The design where alias dereferencing is handled outside of 
+                backends through subordinate searches on aliased entries would 
+                fragment the sorted resultset returned by search cursors.  If a
+                parent search returning sorted entries stops at some alias entry
+                to return the sorted entries of a subordinate search then there
+                will be a discontinuity in sorting order.  The subordinate 
+                search would not return entries in order after the last entry
+                returned by the parent.  Likewise when the subordinate returns
+                its last entry, there is a discontinuity again in the sorting 
+                order when the parent search resumes where it left off.  Without
+                caching the entire set of entrys then iterating over them once 
+                again for the return to the client in sorted order, it would be
+                virtually impossible to manage server side entry sorting 
+                controls.  Doing so would unnecessarily consume both space and
+                time.
+            </para>
+            
+            <para>
+                ????
+                In the case of implementing alias dereferencing within the Jdbm 
+                based backend, modjdbm, the problem would still persist.
+            </para>
+            </section>
+        </section>
+    </section>
+    
+    <section>
+        <title>Alias Handling Implementation</title>
+        
+        <para>
+            We have decided to embed alias handling functionality into backends
+            enabling rapid alias handling without allowing the creation of DIB 
+            crossing aliases.  The following section elaborates on the details
+            of the implementation with respect to the default backend based on
+            JDBM.  The implementation of the JDBM backend is applicable to the
+            the implementation for the BerkeleyDB backend due to their extreme
+            similarity.  Other backend modules like the jdbc backend will be
+            very different.
+        </para>
+        
+        <section>
+        <title>The Implementation of Alias Dereferencing in Backends</title>
+        
+        <para>
+            The implementation shall separate alias handling into categories
+            based on scope and dereferencing modes.  These search parameters
+            are request specific.  The scope parameter is made available to the
+            backend through the search method as a method argument.  The alias
+            dereferencing mode is not and must be made available.  The lack of
+            access to the dereferencing mode parameter raises some questions
+            regarding the handling of request parameters.  There presently is
+            no localized access mechanism for request scope parameters.  In 
+            contrast, session scope parameter alteration and access is 
+            centralized and well defined within the ClientManager and its
+            helper interfaces.  Perhaps there needs to be a similar central
+            facility to access and alter mutable request parameters.  The answer
+            to this question is beyond the scope of this document and it needs
+            to be resolved before implementing any request parameter dependent
+            functionality such as alias dereferencing.
+        </para>
+        
+        </section>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+        </para>
+
+        <para>
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/output-module.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/output-module.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,194 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.6 $</revnumber>
+                <date>$Date: 2003/05/03 01:45:23 $</date>
+                <revdescription>
+                    <para>
+$Log: output-module.xml,v $
+Revision 1.6  2003/05/03 01:45:23  bearcej
+Refix image URLs
+
+Revision 1.5  2003/05/03 01:43:08  bearcej
+Add maven file to help generate server site.
+Modify graphic links again.
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.4  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.3  2003/04/08 02:29:55  bearcej
+* Clean up project.properties
+* Fix logos in project.xml
+* Fix links to image files in docs
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.3  2003/03/15 17:18:35  bearcej
+Fixed figure tags to conform to simple docbook.
+Modified Files:
+ Tag: ALPHA-0_7
+    output-module.xml
+
+Revision 1.1.2.2  2003/03/10 23:43:32  akarasulu
+Moved links so that image references point to /image/design.  Note that
+references do not have a [.] in front since the maven driven transforms
+automatically append [.] to references.
+
+Revision 1.1.2.1  2003/03/10 23:24:21  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.2  2003/03/09 01:33:58  akarasulu
+Completed OuputModule documentation and refactored annon inner class for
+handler into named inner class.
+
+Revision 1.1.2.1  2003/03/03 04:49:25  akarasulu
+Added as placeholders for now.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+            The OutputModule implements the OutputManager service interface used
+            to transmit LDAPv3 ASN.1 BER encoded message responses to clients on
+            a server OutputStream.  The module is implemented as a stage which
+            asynchronously processes OutputEvents carrying the message to 
+            transmit as well as the client key used to identify the client 
+            stream.
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+            The OutputManager interface extends two subinterfaces: 
+            OutputListener and ClientManagerSlave.  The OutputListener interface
+            gives the OutputManager its event processing character by defining
+            a single method called writeResponse to process its OutputEvent
+            argument.  The OutputModule which implements OuputManager, is 
+            implemented as a OutputEvent processing stage.  The OutputEvents are
+            delivered through the OutputListener.writeResponse() method which
+            returns immediately after enqueueing the event onto the stage event
+            queue.
+        </para>
+        
+        <para>
+            The ClientManagerSlave interface is extended by the OutputManager to
+            prevent containers of this module from detecting a cyclic dependency
+            between the ClientManager and eventually this module.  This 
+            interface essentially defines a method that is called by the 
+            ClientManager on initialization to set the handle of these slave on
+            the ClientManager service.  This avoids the use of the conventional
+            service life-cycle method to initialize the handle on the 
+            ClientManager.  If the handle were gotten by the ServiceManager fed
+            into the service method, the cyclic dependency would be caught and
+            the server would not start.  Hence this module is a slave to the 
+            ClientManager since it must have a member handle initialized by it.
+        </para>
+        
+        <para>
+            The OutputManager interface defines three other service methods.  
+            These are: write(), register() and unregister().  The write() method
+            is a special synchronous flush of a response message which bypasses
+            the asynchronous stage processing of the OutputEvent.  It directly
+            requires the client key and the message PDU in the form of an 
+            InputStream with both provided as arguments.  The synchronous write
+            method is a work around to bypass staged processing to facilitate
+            the synchronous delivery of search responses.  The search request 
+            process calls this method to maintain the order of responses in a
+            train of search entry response message envelopes.  The other two 
+            methods are defined to enable the registration and unregistration
+            of client's that must have response messages delivered to them.  
+            When client's connect to the server, the ListenerModule adds the
+            client to the ClientManager.  The ClientManager then on behalf of
+            the listener registers the client's InputStream with the 
+            InputManager and registers the client's OutputStream with the 
+            OutputManager.  This enables the stage pipling to go full circle by
+            recieving input requests, processing then, and returning the 
+            responses to the client.
+        </para>
+        
+        <figure>
+            <title>OutputModule's OutputEventHandler Sequence Diagram</title>
+            <graphic fileref="../images/OutputEventHandlerHandleEvent.gif"/>
+        </figure>
+
+        <para>
+            This stage is extremely simple.  Like other stages its driver 
+            dequeues an incoming event, and creates an annonymous inner Runnable
+            to drive the call to the stage event handler's handleEvent method in
+            the execution context of a stage worker thread.  The OutputEvent is
+            provided as the sole argument to the handleEvent() method on the 
+            EventHandler.  The gist of the work done within the write() method 
+            of the module which is called by the handler.  Essentially the 
+            handler just extracts the client key and the InputStream to the 
+            response buffer from the OutputEvent argument.  It then feeds them 
+            both into the synchronous write() method as arguments.  The same 
+            write() method used by the search request processor to synchronously
+            deliver response messages is used to asynchronously deliver messages
+            when driven by a worker thread.  The trivial eventHandler method is 
+            depicted in the sequence diagram above.
+        </para>
+        
+        <figure>
+            <title>OutputModule's write() Method Sequence Diagram</title>
+            <graphic fileref="../images/OutputModuleWrite.gif"/>
+        </figure>
+        
+        <para>
+            As can be seen from the sequence diagram above depicting the flow of
+            the write() method, the client key is used to lookup the OuputStream
+            of the client.  Then after acquiring a lock on the output lock 
+            object, a while loop is entered which reads from the InputStream 
+            argument.  The InputStream reads from the encoded response message
+            buffer constructed by the upstream encoder stage.  Once the buffer
+            has been consumed or the client key expires the loop terminates.  
+            For all practical purposes it can now be concluded that the reponse
+            message has been delivered to the client.  notifyAll() is then 
+            called to awaken threads blocked waiting for the OutputStream of the
+            client. 
+        </para>
+        
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+            This module is pretty solid.  
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+            None so far.
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/protocol-module.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/protocol-module.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,979 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+	"http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+	<title>Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: akarasulu $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.7 $</revnumber>
+                <date>$Date: 2003/08/22 21:15:54 $</date>
+                <revdescription>
+                    <para>
+$Log: protocol-module.xml,v $
+Revision 1.7  2003/08/22 21:15:54  akarasulu
+Merged changes made in the USING_PROVIDER_FRAMEWORK branch of the
+server.  I don't suspect she will be working well at this point.  Lots
+of overhauling required now.
+
+Revision 1.3.2.2  2003/05/17 16:58:52  akarasulu
+started an addendum section for refactorings which will be used to refactor
+this document to synch it up with the code changes currently under way w/i
+the USING_PROVIDER_FRAMEWORK brach.
+
+Revision 1.3.2.1  2003/05/05 17:28:54  akarasulu
+Moving towards the request handler flywieght pattern away from the
+per request dedicated processor based design of the protocol engine.
+
+Revision 1.3  2003/04/08 02:29:55  bearcej
+* Clean up project.properties
+* Fix logos in project.xml
+* Fix links to image files in docs
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.4  2003/03/15 22:30:30  bearcej
+Fix last figure href.
+
+Revision 1.1.2.3  2003/03/15 17:16:47  bearcej
+Fixed figure tags to conform to simple docbook.
+Modified Files:
+ Tag: ALPHA-0_7
+	protocol-module.xml
+
+Revision 1.1.2.2  2003/03/10 23:43:32  akarasulu
+Moved links so that image references point to /image/design.  Note that
+references do not have a [.] in front since the maven driven transforms
+automatically append [.] to references.
+
+Revision 1.1.2.1  2003/03/10 23:24:21  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.6  2003/03/09 00:36:54  akarasulu
+Finished encoder docs and refactored inner annonymous handler to be a
+named inner class.
+
+Revision 1.1.2.5  2003/03/08 23:38:42  akarasulu
+Completed protocol engine documentation along with subsections for the
+various protocol request processors used.
+
+Revision 1.1.2.4  2003/03/07 20:50:47  akarasulu
+*** empty log message ***
+
+Revision 1.1.2.3  2003/03/07 20:49:33  akarasulu
+Adding Bind docs
+
+Revision 1.1.2.2  2003/03/06 23:11:26  akarasulu
+Proof read decoder module and added some content to protocol module docs.
+
+Revision 1.1.2.1  2003/03/03 04:49:25  akarasulu
+Added as placeholders for now.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+	<abstract>
+		<para>
+			The ProtocolModule is the heart of the server.  It contains the
+			logic required to service each LDAPv3 protocol request and if need
+			be construct one or more response PDUs.  The module implements the
+			RequestListener interface to have RequestEvents delivered for 
+			processing.  It is implemented as a stage and consequently can 
+			process several requests in parallel.
+		</para>
+	</abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add sequence diagrams.
+            </para></listitem>
+            <listitem><para>
+                Add link to relavent rfc 2251 sections.
+            </para></listitem>
+            <listitem><para>
+                Add diagrams showing the composition of both request and 
+                where relavent response envelopes.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Background</title>
+        
+        <para>
+            The decoding stage of the server delivers decoded Requests to the 
+            protocol stage using RequestEvents.  The request events carry the
+            Request and the ClientKey for the client submitting the Request.  It
+            is the responsibility of the protocol stage to service each request
+            using the resources available.
+        </para>
+            
+        <para>
+            Based on the message type, the manner in which Requests are serviced
+            differs.  The majority of Request messages produce a single Response
+            as a reply.  AbandonRequest, and UnbindRequests produce no Response 
+            at all.  Conversely, in the course of servicing a SearchRequest many
+            Responses of various message types may be generated in reply.  
+            According to RFC 2251 a SearchRequest must reply with a 
+            SearchResponseDone to mark the end of the reply.  Before 
+            transmission of the SearchResponseDone, zero or more (in any order)
+            SearchResponseEntry, SearchResponseReference and ExtendedResponses
+            may be returned to the client.  These vastly differing modes of 
+            Request handling makes it difficult to devise a generalize mechanism
+            to process all requests.  The best approach would be to devise a 
+            processing scheme for each category of request.
+        </para>
+        
+        <para>
+            The protocol imposes no requirement enforcing ordered response 
+            delivery between request messages.  The message sequence identifier
+            is used to prevent the protocol from having to impose such 
+            requirements.  Hence the response to request 3 may be returned 
+            before the response to request 1.  The server's seda architecture 
+            takes advantage of this be enabling the asynchonous staged delivery
+            of responses back to a client.  Whenever a response to a request is
+            composes it is returned back to the client asynchronously without 
+            concern for the order of delivery.  The multiresponse nature of the
+            search request does however impose a response delivery order within
+            a message request.  All Responses delivered in reply to a 
+            SearchRequest have the same message sequence identifier.  This way 
+            they are all associated with the same SearchRequest.  Our only 
+            problem is the need to return some search responses before others 
+            in a asynchronous response delivery environment.
+        </para>
+        
+        <para>
+            The SearchRequest presents many design hurdles to overcome.  The 
+            server must synchronize the delivery of search responses within a 
+            SearchRequest.  Searches using the sort control extention cannot 
+            return sorted entries without synchronous delivery to maintain 
+            sorting order.  Furthurmore we must ensure the last response sent to
+            be the SearchResponseDone message.
+        </para>
+    </section>
+    
+    
+    <section>
+        <title>Architecture</title>
+        
+        <para>
+			As a stage the work of processing RequestEvents is delegated to the
+			stage worker threads.  The worker threads as in other stages drive
+			the synchronous call to the stage's event handler.  The stage event
+			handler accepts event's and specifically with regard to this module
+			it requires events to be RequestEvents.  So far these aspects to the
+			module's architecture are really based on the architecture of a 
+			stage.
+        </para>
+        
+        <para>
+            The use of the new Message Framework encapsulates protocol request
+            parameters and other message parameters within a Request object.
+            The Request object can be used to store request processing state 
+            rather than embedding state tracking into some kind of request 
+            processor object for each request.  The FlyWeight design pattern 
+            can be used to have a specific processor instance service all 
+            requests of a particular request message type.  Previously before 
+            the Message Framework, a specific processor would be instantiated to 
+            track and process each request.  Once processing was complete the
+            processor memory would be reclaimed.  Each incomming request would 
+            instantiate a processor.  The processor itself served to encapsulate
+            request protocol parameters like a request bean to prevent other 
+            parts of the system from having to deal with ASN.1 compiler stubs.
+            Now with a clean explicit API to represent messages independent of 
+            the ASN.1 stub compiler and BER library used we can expose a Request
+            bean rather than a processor wrapping a stub class.
+        </para>
+            
+        <para>
+			Still the specific processing of each type of protocol requests is 
+            best handled using separate request processor implementations.  For
+            now we will just call them handlers.  They really are specific 
+            request message type handlers with the new Message framework.  A 
+            handler unlike a processor, can be used to safely handle several
+            requests concurrently whereas a processor is dedicated to an 
+            individual request.  We can easily find the request handler for a
+            request based on the message type enumeration.  A RequestHandler 
+            interface is designed to standardize handler operations.  The 
+            stage's handler selects the specific request handler for the 
+            respective request off of a switch on the protocol message type.  
+            After assigning the Request to a handler, generic handling interface
+            methods can be used to manage the process without regard to type 
+            specifics.
+        </para>
+        
+        <para>
+			The work of a request handler is performed in a simple handleRequest 
+            method that returns a potentially null handle on a Response.  The 
+            stage's event handler simply calls this method on the processor.  If
+            the request requires a response the response is handed off to the 
+            next downstream stage (the decoder) in the form of a ResponseEvent.  
+            The response if one is to be returned is the LDAPMessage object 
+            returned by the process method.
+        </para>
+		
+		<para>
+			The use of processors to handle requests based on type is a form of
+			the Reactor design patten and has several benefits.  First and 
+			foremost it separates the handling of each request out into separate
+			classes thereby minimizing complexity.  Second it provides a place 
+			where request specific parameters can be managed, like the request 
+			PDU envelope (an LDAPMessage instance).  Finally a common super 
+			interface standardizes request event handling by manipulating each
+			request processor implementation through the methods of this common 
+			interface.
+		</para>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+			The best approach to describing the implementation is to generally 
+			describe the event handling from an enqueue onto this stage's queue 
+			on upto the death of a stage worker thread without dealing with the
+			specifics of each processor.  The specifics of each processor's 
+			implentation is handled in separate request specific sections.
+        </para>
+        
+        <para>
+			As noted, this module is implemented as a stage to enable parallel
+			processing of multiple request events.  Event processing is handled
+			asynchronously by stage worker threads.  The RequestListener's
+			requestReceived method implementation simply enqueues the 
+			RequestEvent argument onto the stage event queue.  Hence, the 
+			requestReceived method called by an upstream decoder stage worker 
+			thread returns immediately.
+        </para>
+        
+        <para>
+			Once the RequestEvent is enqueued, the stage dedicates a worker to 
+			drive an instance of an annonymous Runnable class.  The Runnable's 
+			run method, as in all stages, calls the stage's EventHandler passing
+			it the RequestEvent.  A named inner class called a 
+			RequestEventHandler is defined and an instance of it is instantiated 
+			within the module's default constructor.  This is the EventHandler 
+			implementation used for this stage.  The RequestEventHandler's 
+			handleEvent method is called by the annonymous Runnable's run 
+			method.
+        </para>
+		
+		<figure>
+            <title>ProtocolModule's RequestEventHandler Sequence Diagram</title>
+	    <mediaobject><imageobject>
+            <imagedata fileref="images/RequestEventHandlerHandleEvent.gif"/>
+	    </imageobject></mediaobject>
+		</figure>
+		
+		<para>
+			The primary function of the eventHandler implementation is to 
+			instantiate the correct RequestProcessor matching the request and 
+			call the RequestProcessor.process() method.  Initially the handler
+			checks that the event is an instance of RequestEvent, and accesses
+			the parameter's of the request packaged into the RequestEvent.  
+			These include the request envelope as an LDAPMessage, the ClientKey,
+			and the message type value used in the switch to follow.  The switch
+			is on the message type and it is used to instantiate the appropriate
+			RequestProcessor.  Once the RequestProcessor is set the current 
+			thread of execution is bound to the processor within a ThreadLocal
+			to access request parameters during the coarse of processing the 
+			request without passing around several variables.  The service and
+			initialize life-cycle methods of the RequestProcessor are called to 
+			prepare it for the process invokation.  The call to the process 
+			method returns a handle on a response if one exists.  If a response
+			is to be returned by the processor it is packaged into a 
+			ResponseEvent and delivered to the next downstream stage, the 
+			encoder.  Whether or not a response is generated the executing 
+			thread is disassociated with the RequestProcessor after the call
+			to process.  After this last step the handler returns.  The entire
+			sequence is depicted in the sequence diagram above.  The specifics
+			of each RequestProcessor's process method is discussed in details
+			within the sections to follow.
+		</para>
+
+		<section>
+        	<title>AbandonRequestProcessor</title>
+			
+			<para>
+				The purpose of the AbandonRequestProcessor is to implement the 
+				abandon protocol operation.  This processor is not yet 
+                implemented.  According to the protocol this operation does not
+                produce as response.
+			</para>
+		</section>
+
+		<section>
+        	<title>AddRequestProcessor</title>
+			
+			<para>
+				The purpose of the AddRequestProcessor is to implement the add 
+                protocol operation.  This operation adds a single new entry at 
+                the base of an existing entry.  According to the protocol this 
+                operation must produce a response indicating success or failure.
+			</para>
+            
+            <para>
+                The processor is created by passing the client's key and the 
+                request envelope to the constructor.  On initialization the 
+                response envelope is allocated and populated minimally with the
+                message sequence id and an empty AddResponse object.  The 
+                service method initializes handles on dependent modules: the
+                UnifiedBackend and the EventManager.
+            </para>
+            
+			<figure>
+				<title>
+					AddRequestProcessor's process() Sequence Diagram
+				</title>
+				<mediaobject><imageobject>
+				<imagedata fileref="images/AddRequestProcessorProcess.gif"/>
+				</imageobject></mediaobject>
+			</figure>
+		
+            <para>
+                Before wasting time and resources by investigating the contents
+                of the request envelope the processors process() method checks 
+                to see if the entry to be added already exists by calling the 
+                hasEntry method of the UnifiedBackend.  The protocol request 
+                contains the set of attribute identifiers and their single or 
+                multiple values.  These values are extracted from the request 
+                envelope using the stubs generated by the Snacc4J compiler.  
+                These attributes and values are added to an empty LdapEntry 
+                returned by the UnifiedBackend.  The LdapEntry implementation is 
+                constructed by the AtomicBackend associated with the naming 
+                context under which the name of the entry to be added exists.
+                The UnifiedBackend automatically routes these kinds of calls to
+                the naming context's AtomicBackend.  Once populated a pre-event
+                announcement via the fireBefore() method of the EventManager is
+                made.  The create method on the UnifiedBackend is called to 
+                create (add) this new attribute.  Immediately there after 
+                another post-addition protocol event is fired via the 
+                EventManager's fireAfter method.  See the sequence diagram for
+                high level view of the add operation's success pathway.
+            </para>
+		</section>
+        
+		<section>
+        	<title>BindRequestProcessor</title>
+			
+			<para>
+				The purpose of the BindRequestProcessor is to implement the 
+				bind protocol operation.  The processor depends on the 
+				EventManager, UnifiedBackend and AuthenticationManager services.
+                The bind operation generates a bind response no matter what the
+                results of the bind operation.
+			</para>
+			
+			<para>
+                An instance of the bind processor is created by passing the 
+                ClientKey, a handle to the ClientManager service and the 
+				LDAPMessage request as arguments to the constructor.  The call 
+                to initialize allocates a response LDAPMessage to the bind 
+                request which is an empty envelope.  Some preparations are made
+				to the request envelope like adding the message sequence id, and
+				initializing the BindResponse object.  The service method is
+				next in line and is called by the handler after calling 
+				initialize.  The service method is used to pass service handles 
+				to the processor.  The handle to the AuthorizationManager, the
+				UnifiedBackend and the EventManager are acquired here.
+			</para>
+			
+			<figure>
+				<title>
+					BindRequest Processor's process() Sequence Diagram
+				</title>
+				<mediaobject><imageobject>
+				<imagedata fileref="images/BindRequestProcessorProcess.gif"/>
+				</imageobject></mediaobject>
+			</figure>
+		
+			<para>
+				After all life-cycle methods are invoked the process method is 
+				called.  The highlevel sequence of events within the process() 
+                method are shown in the sequence diagram above.  Currently the 
+                only available authentication method is simple.  SASL based 
+                authentication is not available.  Therefore the diagram 
+                represents the steps towards successfully authenticating a user 
+                via the simple method pathway.  It does not show error handling 
+                functionality within the processor.  SASL authentication 
+                attempts and various exceptions which may occur result in the
+                construction of an alternative LDAPMessage response object.  In 
+                these cases the error or unavailability of an operation result 
+                in a response returning the respective LDAP error code along 
+                with a descriptive message regarding the error.
+			</para>
+            
+            <para>
+                The normal operational pathway for the processor first results
+                in the construction of a bind protocol event which is delivered
+                to protocol event listeners via the EventManager service.  This
+                pre-event announcement is depicted in call 1.1 of the diagram.
+                The processor then invokes the loginSimple method of the 
+                AuthenticationManager which either results in a LoginException 
+                or returned a valid Pricipal object.  Presuming the correct
+                credentials were supplied, the processor then creates the 
+                client's session by calling create on the ClientManager service
+                interface.  Thereafter the processor fires a post-event 
+                announcement using the fireAfter method on the EventManager and
+                returns.
+            </para>
+            
+            <para>
+                Exceptional conditions at any point result in the return of an
+                envelope packaged with the LDAP error code and a descriptive
+                message.
+            </para>
+		</section>
+
+        <section>
+        	<title>CompareRequestProcessor</title>
+			
+			<para>
+				The purpose of the CompareRequestProcessor is to implement the 
+				compare protocol operation.  The processor depends on the 
+				EventManager, and the UnifiedBackend services.  The purpose of
+                the operation is to evaluate an attribute assertion on an entry 
+                specified by DN.  The compare operation generates a compare 
+                response no matter whether the operation succeeds, fails or
+                generates an exception during processing.
+			</para>
+			
+			<figure>
+				<title>
+					CompareRequestProcessor's process() Sequence Diagram
+				</title>
+				<mediaobject><imageobject>
+				<imagedata fileref="images/CompareRequestProcessorProcess.gif"/>
+				</imageobject></mediaobject>
+			</figure>
+		
+			<para>
+                The processor is instantiated by passing the client key and the
+                request message envelope as arguments to the constructor.  On
+                initialization the response message envelope is allocated and
+                populated with the message sequence id and an empty 
+                CompareResponse.  The handles on the dependent UnifiedBackend 
+                and EventManager services are acquired after initialization with
+                a call to the processor's service method.
+			</para>
+            
+            <para>
+                Once the process method is called the processor attempts to 
+                resusitate the LdapEntry to be tested using the attribute value
+                assertion contained in the compare request envelope.  Next the
+                entries attributes are tested to see if they comply with the
+                assertion.  The results of the test are then packaged into the
+                compare response returned within the LDAPMessage envelope.  
+                Before returning the response the process method announces 
+                operation completion via the EventManager's fireAfter method.
+            </para>
+
+            <para>
+                Exceptional conditions at any point result in the return of an
+                envelope packaged with the LDAP error code and a descriptive
+                message.
+            </para>
+        </section>
+        
+        <section>
+        	<title>DelRequestProcessor</title>
+			
+			<para>
+				The purpose of the DelRequestProcessor is to implement the 
+				delete protocol operation.  The processor depends on the 
+				EventManager, and the UnifiedBackend services.  The purpose of
+                the operation is to remove a single entry specified by DN.  The
+                operation according to the LDAPv3 protocol will fail if the
+                target entry has children.  The delete operation generates a 
+                response no matter whether the operation succeeds, fails or
+                generates an exception during processing.
+			</para>
+			
+			<figure>
+				<title>
+					DelRequestProcessor's process() Sequence Diagram
+				</title>
+				<mediaobject><imageobject>
+				<imagedata fileref="images/DelRequestProcessorProcess.gif"/>
+				</imageobject></mediaobject>
+			</figure>
+		
+			<para>
+                The processor is instantiated by the protocol engines stage
+                event handle by a call to the constructor passing in the client
+                key and the request message envelope.  On initialization the
+                response message envelope is allocated and populated with the
+                message sequence id and an empty delete response object.  
+                Handles to the dependent UnifiedBackend and EventManager 
+                services are initialized on the call to the service life-cycle
+                method before the process method is called.
+			</para>
+            
+            <para>
+                The process method first resusitates the target entry to delete
+                from the UnifiedBackend with a read call that returns a handle
+                on an LdapEntry.  This confirms the existance of the entry and
+                facilitates the call to delete() which requires an LdapEntry as
+                the argument.  Then a pre-event announcement is made via a call
+                to the fireBefore method of the EventManager.  The call to the
+                delete method is made using the returned LdapEntry.  Finally
+                before returning the response envelope confirming the deletion,
+                a post protocol operation is announced via the fireAfter method
+                of the EventManager.
+            </para>
+            
+            <para>
+                Exceptional conditions at any point result in the return of an
+                envelope packaged with the LDAP error code and a descriptive
+                message.
+            </para>
+		</section>
+        
+        <section>
+        	<title>ExtendedRequestProcessor</title>
+			
+			<para>
+				The purpose of the ExtendedRequestProcessor is to implement the 
+				extended protocol operation.  The processor depends on the 
+				EventManager, and the UnifiedBackend services.  The purpose of
+                the operation is to enable protocol compliant operation 
+                extentions.  The request contains an OID and a binary payload
+                holding the parameters required in an operation specific format.
+                The operation is simply a pass through to execute some type of
+                application specific or server specific operation.  The extended
+                operation generates a response no matter whether the operation 
+                succeeds, fails or generates an exception during processing.
+			</para>
+			
+			<figure>
+				<title>
+					ExtendedRequestProcessor's process() Sequence Diagram
+				</title>
+				<mediaobject><imageobject>
+                		<imagedata fileref="images/ExtendedRequestProcessorProcess.gif"/>
+				</imageobject></mediaobject>
+			</figure>
+		
+			<para>
+                The processor is instantiated by passing the client key, the
+                request message envelope and a handler map to the constructor.
+                The handler map contains a map of OID Strings to ExtReqHandler
+                instances.  These handlers are simple implementations of 
+                functions with assigned OIDs.  They take the payload as a byte 
+                array argument which is extracted from the extended request 
+                envelope and return a byte array.  The returned byte array 
+                represents the extended response payload which is packaged into
+                the response envelope returned by the processor's process 
+                method.  These extended request handlers and the OID to handler
+                map are created by the protocol engine during the module's 
+                configuration life-cycle phase.
+			</para>
+            
+            <para>
+                By providing the extended request handler map in the constructor
+                the processor can invoke the appropriate handler on the request
+                payload and hence respond to the request using the resultant 
+                response payload.  Following instantiation the processor is 
+                initialized creating the empty extended request message envelope
+                populated with the message sequence id and an empty 
+                ExtendedResponse object.  Then before the process method is 
+                called the service method is called to acquire handles on the 
+                dependent EventManager service.  It is very likely that other
+                service dependencies will exist however since we have not 
+                implemented any of these extended request handlers we know of 
+                none for now.  Eventually it will be necessary to enable 
+                extended request handlers with handles to other dependent 
+                modules.  This will require a change to the extended request 
+                handler or to its implementation to have it implement the 
+                Serviceable interface.  Handles to dependent services within 
+                the server to do the work of processing the extended request
+                can be obtained through this standard Avalon life-cycle method.
+            </para>
+            
+            <para>
+                The process method immediately announces the arrival of an 
+                extended request by calling the fireBefore method of the 
+                EventManager service.  It then calls the handle method of the
+                OID specific handler using the extracted payload of the request
+                envelope.  Then before the resultant payload is packaged into
+                the response envelope and returned the fireAfter method of the
+                EventManager is called.
+            </para>
+            
+            <para>
+                Exceptional conditions at any point result in the return of an
+                envelope packaged with the LDAP error code and a descriptive
+                message.
+            </para>
+		</section>
+        
+        <section>
+        	<title>ModifyDNRequestProcessor</title>
+			
+			<para>
+				The purpose of the ModifyDNRequestProcessor is to implement the 
+				modify DN protocol operation.  The processor depends on the 
+				EventManager, and the UnifiedBackend services.  The purpose of
+                the operation is to change the name of an entry and consequently
+                the DN of every descendant entry.  The modify DN operation can
+                optionally change the Rdn attribute used by the target entry.  
+                The operation generates a response no matter whether it 
+                succeeds, fails or generates an exception during processing.
+			</para>
+			
+			<figure>
+				<title>
+					ModifyDNRequestProcessor's process() Sequence Diagram
+				</title>
+				<mediaobject><imageobject>
+                		<imagedata fileref="images/ModifyDNRequestProcessorProcess.gif"/>
+				</imageobject></mediaobject>
+			</figure>
+		
+			<para>
+                The call to the constructor with a client key and the request
+                message envelope is made by the protocol engine stage's event
+                handler.  Once instantiated the call initialize again by the
+                event handler allocates a response message envelope populated
+                with the message sequence id and an empty ModifyDNResponse
+                object.  The call to the service method next by the handler 
+                initializes processor member variables referring to the 
+                dependent services.
+			</para>
+            
+            <para>
+                The process method of the processor first checks to see if the
+                target entry whose DN is to be modified exists.  If the entry
+                exists a pre modify DN announcement is made via the fireBefore
+                method of the EventManager service.  Next the modifyRdn method 
+                is called on the UnifiedBackend service and then a call is made
+                to fireAfter to announce the completion of the operation on the
+                EventManager service.  The results of the operation are returned
+                after being packaged into the response envelope.
+            </para>
+            
+            <para>
+                Exceptional conditions at any point result in the return of an
+                envelope packaged with the LDAP error code and a descriptive
+                message.
+            </para>
+		</section>
+        
+        <section>
+        	<title>ModifyRequestProcessor</title>
+			
+			<para>
+				The purpose of the ModifyRequestProcessor is to implement the 
+				modify protocol operation.  The processor depends on the 
+				EventManager, and the UnifiedBackend services.  The purpose of
+                the operation is to change, remove or add a set of attribute 
+                value pairs to an entry specified by DN.  The operation 
+                generates a response no matter whether it succeeds, fails or 
+                generates an exception during processing.
+			</para>
+			
+			<figure>
+				<title>
+					ModifyRequestProcessor's process() Sequence Diagram
+				</title>
+				<mediaobject><imageobject>
+				<imagedata fileref="images/ModifyRequestProcessorProcess.gif"/>
+				</imageobject></mediaobject>
+			</figure>
+		
+			<para>
+                The processor is instantiate via a standard constructor call 
+                taking the client key and the request message envelope as its
+                arguments.  The initialize method call made by the protocol 
+                engine stage event handler allocates a response message envelope
+                populated with the message sequence id and an empty 
+                ModifyResponse.  Finally before assigning a worker to process
+                the request the service method is called by the handler to 
+                initialize handles on the dependent services required by this
+                processor.
+			</para>
+            
+            <para>
+                Once the worker thread calls process, the target entry is 
+                resusitated from the owning context via a call to the 
+                UnifiedBackend's read method.  Next the resusitated entries
+                are modified according to the changes described by the modify
+                request envelope.  At this point before calling update the 
+                pre modify operation must be fired, yet it seems to have been
+                overlooked.  It needs to be fired.  Next a the call to update 
+                on the UnifiedBackend is made to effect the changes made to the
+                target entry.  Finally before returning the response message 
+                envelope the processor needs to call the fireAfter method on the
+                EventManager service yet again this seems to have been 
+                overlooked.
+            </para>
+		</section>
+        
+        <section>
+        	<title>SearchRequestProcessor</title>
+			
+			<para>
+				The purpose of the SearchRequestProcessor is to implement the 
+				search protocol operation and it is by far the most complex of
+                all the protocol operations.  The processor depends on the 
+				EventManager, the UnifiedBackend, the Encoder and OutputManager 
+                services.  The purpose of the operation is to search the 
+                directory returning zero or more entries or references to 
+                entries while appling search controls to moderate the operation.  
+                The operation generates a response no matter whether it 
+                succeeds, fails or generates an exception during processing.
+			</para>
+            
+            <para>
+                The search operation as mentioned before is the most complex. As
+                a consequence the processor implementation for it is complex as
+                well.  The search operation can return one or more responses to 
+                the client or zero or more entry or referrals.  The search 
+                operation uses different responses for each entry returned, from
+                each referral to a remote entry.  Referral response message 
+                envelopes hence have the url of the entry whereas entry response
+                message envelopes contain the attributes of the entry.  Not to
+                mention these response types, at the end of the operation once 
+                the search has completed a search done response message must be
+                delivered to the client.  The search done response message 
+                envelope has yet another makeup.
+            </para>
+            
+            <para>
+                While managing the delivery, the search processor must take 
+                search controls into account to moderate the operation.  For
+                the time being controls are not fully implemented.  As a 
+                consequence of the need to sychronize the ordered receipt of 
+                messages the search processor must use synchronous message 
+                delivery methods on the Encoder and the OutputModule for all
+                response messages except the final search done response.  These
+                factors complicate the operation of the processor.
+            </para>
+            
+			<figure>
+				<title>
+					SearchRequestProcessor's process() Sequence Diagram
+				</title>
+				<mediaobject><imageobject>
+				<imagedata fileref="images/SearchRequestProcessorProcess.gif"/>
+				</imageobject></mediaobject>
+			</figure>
+		
+			<para>
+                The processor is instantiated via a call to its constructor 
+                using the client key and the request message envelope as 
+                parameters.  The initialize life-cycle method does nothing since
+                the response message processing can be handled usind more than
+                one type of response unlike other protocol operations.
+			</para>
+            
+            <para>
+                Since the processor directly manages and drives the synchronous
+                delivery of all responses except for the final search done 
+                response it must communicate with the Encoder and the 
+                OutputModule directly without relying on staged event processing
+                pathways within the server.  This is why the processor must 
+                initialize a handle on both the Encoder and OutputManager 
+                services as well as the EventManager and UnifiedBackend services
+                during the service life-cycle method.
+            </para>
+			
+            <para>
+                Once processing begins the processor extracts the base DN, 
+                search controls and filter information for the search from the
+                request message envelope.  A pre search event is then fired from
+                the EventManager service's fireBefore method.  Now the processor
+                is ready to call the search method on the UnifiedBackend 
+                service.  The search simply returns a Cursor which requires a
+                loop to extract the candidate entries to return.  Within the 
+                loop synchronous calls are made to special method interfaces 
+                for synchronous processing on the Encoder and OutputManager 
+                services.  Once all the entry and referal response messages have
+                been delivered synchronously to the client, the last search done
+                response message envelope is constructed and returned to the
+                caller.  This last response message envelope is delivered 
+                asynchronously to the client using the standard staged pathways
+                of the server.
+            </para>
+		</section>
+        
+        <section>
+        	<title>UnBindRequestProcessor</title>
+			
+			<para>
+				The purpose of the UnBindRequestProcessor is to implement the 
+				unbind protocol operation.  The processor depends on the 
+				CllientManager only and the handle is passed in the constructor.  
+                The purpose of the operation is to drop a client and destroy 
+                their session and associated resources.  The operation does not
+                generate a response - the client socket drop can be considered
+                the response however clients usually close their connections
+                just after sending this request.
+			</para>
+			
+			<figure>
+				<title>
+					UnBindRequestProcessor's process() Sequence Diagram
+				</title>
+				<mediaobject><imageobject>
+				<imagedata fileref="images/UnBindRequestProcessorProcess.gif"/>
+				</imageobject></mediaobject>
+			</figure>
+		
+			<para>
+                This very simple processor is instantiated via a call to its 
+                constructor requiring as arguments a handle to the ClientManager
+                service, a client key, and the request message envelope.  Both
+                the initialize and service life-cycle methods do nothing at all
+                with empty method bodies.
+			</para>
+            
+            <para>
+                The process method should announce the unbind protocol operation
+                before and after droping the client connection yet this is not
+                the case.  It merely drops the connection through a call to the
+                drop method of the client manager.  It returns null indicating
+                that no response is to be returned to the client.
+            </para>
+		</section>
+        
+    </section>
+    
+    
+    
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+			We need to revaluate whether or not we are using ThreadLocals 
+			correctly.
+        </para>
+
+        <para>
+			We need to cleanup the processor code perhaps wrapping access to
+			the Snacc4J generated classes.  Later when and if we use other ASN.1
+			stubs only the stub wrappers will have to change.
+        </para>
+
+        <para>
+			We may want to look at using the JNDI provider to access backends
+			rather than going directly through the nexus module.  The JNDI 
+			provider may be able to transparently manage and encapsulate the 
+			referal management code on behalf of the protocol engine.
+        </para>
+
+        <para>
+			We need to implement the AbandonRequestProcessor for the Abandon
+			request type.  This will require access to the client's session 
+			which may have to hold the executing processor's which will need to
+			be stopped.  In this case the RequestProcessor implementations will
+			need to have some form of termination variable that can be checked
+			every now and then by the executing thread.  These are problems best
+			solved through aspect oriented techniques.
+        </para>
+		
+		<para>
+			Based on the decisions made on using the JNDI provider inside the
+			processors to access backends we may need to put protocol event 
+			firing hooks into all processors to fire their respective protocol
+			event.
+		</para>
+
+        <para>
+            Need to make all processors consistant in the way they handle
+            reporting errors.  Namely I would like to see stack traces returned
+            to the client only if debugging is enabled.
+        </para>
+        
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+            Pre and Post modify protocol events are not fired by the 
+            ModifyRequestProcessor.
+        </para>
+        
+        <para>
+            Pre and Post unbind protocol events are not fired by the 
+            UnBindRequestProcessor.
+        </para>
+    </section>
+    
+    <section>
+        <title>Adendum</title>
+        
+        <para>
+            In the process of refactoring the protocol module and its processors
+            using message beans from the Common Message API (CMA) we started to
+            use the JNDI provider instead of directly interfacing with the 
+            UnifiedBackend.
+        </para>
+
+        <para>
+            There are some issues that arise when using the JNDI provider 
+            specifically in the area of translating Exceptions back into 
+            LDAPv3 result codes.  Some JNDI exceptions are used to represent
+            multiple result codes.  Result code to JNDI exception mappings are
+            published <ulink url=
+            "http://java.sun.com/products/jndi/tutorial/ldap/models/exceptions.html">
+            here</ulink> on the SUN Java site. There is no way for the protocol 
+            module to tell which result code to use when it encounters an 
+            exception that can map to more than one result code.  Using the 
+            JNDI provider hence results in a loss of resolution.  For example 
+            the OperationNotSupportedException can represent result codes 12 and
+            53 for unavailable critical extensions and an unwillingness to 
+            perform an operation respectively.  Hence when the protocol engine 
+            catches a OperationNotSupportedException on calls to the JNDI 
+            interfaces there is no way for it to determine the cause of the 
+            exception.  The protocol engine cannot associate the error with a 
+            Control or Extended opertation that is unsupported with a situation
+            that the server implementation specifically does not support like
+            the deletion of a suffix entry.  We have solved this problem for the
+            time being by embedding the result code enumeration value into the 
+            exception message.  Since the result code can range from 1 to 80 
+            there are at most 2 digits required to represent it within the 
+            message.  We use a 4 character prefix which holds the 2 digit result
+            code value surrounded by '[' ']' brackets.  When handling exceptions 
+            this 4 character prefix is detected if present and the result code
+            is extracted from the exception message.  At the present time this 
+            is a kludgy solution yet it saves us the hastle of extending JNDI 
+            exceptions only to add a result code property to it.
+        </para>
+        
+        <para>
+            With the result code situation above we could have taken another
+            approach.  The code throwing the exception could store the response
+            code directly in the response by accessing the response being 
+            processed.  This will spread the code around involved with 
+            manipulating or building the response rather than centralizing it.  
+            These approaches need to be considered thoroughly before commiting 
+            to any one of them.  Regardless of the approach the region 
+            generating the exception needs to be aware of the result code to 
+            associate with the exception.  Some of this code is within the JNDI
+            provider, some within the nexus and some deep down within the 
+            backends.  The only difference with giving access to the response 
+            is that we enable its alteration within other modules.  There is no 
+            way to manadate every peice of code throwing an exception to use 
+            whatever mechanism we choose.  It would be nice to be able to have a
+            means to enforce it but this is not easy.  Nevertheless before 
+            sending the response to the encoder the handlers of the protocol
+            module need to detect whether or not the result code was set by the
+            exception handling code in the ldap result of a response.  If it did
+            not some default result code would need to be used instead.
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/request-session-scopes.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/request-session-scopes.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,273 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Request and Session Scope Management</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.2 $</revnumber>
+                <date>$Date: 2003/05/03 01:21:32 $</date>
+                <revdescription>
+                    <para>
+$Log: request-session-scopes.xml,v $
+Revision 1.2  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.1  2003/04/04 01:40:16  akarasulu
+Added more to search operation document yet temporarily suspended it to
+start working on request scope management design which all operations will
+eventually depend upon.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+            The management of session and request state requires distinct
+            structures within the server design.  Read only and mutable 
+            parameters within either scope must be accessed and modified 
+            respectively.  The server must provide interfaces through which
+            various components within the system uniformally perfrom such 
+            operations.  These interfaces and access to the objects implementing
+            them the should be centralized and coherent.
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Background</title>
+        
+        <para>
+            The ClientManager interface contains methods used to access a 
+            client's session object.  The ClientManager design promotes 
+            centralized access to ClientSession interfaces through a set of
+            methods designed to retrieve the session using a ClientKey or by
+            using the context of the caller's thread.  The design standardizes
+            and centralizes access to session data without requiring other
+            server modules to pass around the session object or the client key
+            used to retrieve the session.  The design used to manage session 
+            scope parameters would offer the same advantages when used to manage
+            access and modify operation on request scope parameters. 
+        </para>
+        
+        <para>
+            Request information unlike session data changes based on the nature
+            of the protocol operation.  For example a search operation will 
+            contain a search filter and an add operation will not but will have
+            the new entry to add to the DIT.  These differences make it 
+            difficult to devise a standard interface to manage request
+            parameters.  Using an associative key value pair base Map interface 
+            as the basis for the access and storage model standardises the 
+            interface across request.  However the Map interface compromises 
+            security by enabling both access and modification operations on 
+            these parameters regardless of whether they are read only or 
+            mutable.  Use of the Map interface implies weakly typed associations
+            for protocol defined request parameters.  The types of parameters
+            are implicit when accessed or altered as objects in a Map.  Strong
+            explicit types for predefined request parameters should be used if
+            we know the type.  Weak type associations with a Map should only be
+            used for user defined request parameters that are not predefined.
+        </para>
+        
+        <para>
+            The request specific processors within the ProtocolEngine are 
+            responsible for extracting request parameter from a request PDU
+            demarshalled LDAPMessage envelope.  The envelope class is a
+            Java stub class generated by the Snacc ASN.1 compiler.  By leaving
+            parameter extraction up to the processor, the current design 
+            increases the coupling between parameter extraction and request 
+            processing which could be two completely separate phases.  The high
+            degree of coupling imposes a dependency on Snacc API's within the
+            ProtocolEngine.  The only two modules within the server which should
+            have these dependencies are the Encoder and Decoder modules.  It 
+            makes sense to localize parameter extraction from a request PDU 
+            within the decoder module to spare the protocol engine.  The 
+            demarshaling from BER encoded streams handled in the Decoder module
+            should be followed by parameter extraction into a standard request
+            object independent of the ASN.1 BER library used.  The protocol 
+            engine's processors only process requests using these agnostic 
+            request objects without Snacc dependencies, they are not responsible 
+            for managing the creation of the request.  The design extrapolated 
+            to the response phase would require a response object for the 
+            respective request object (if required by the protocol).  The 
+            packaging of response parameters into the Snacc specific LDAPMessage
+            envelope would then be delegated to the Encoder which is already 
+            dependant on Snacc APIs.  The localization of all ASN.1 dependencies
+            to the Decoder and the Encoder module implementations, makes 
+            switching ASN.1 libraries easier by only requiring the Encoder and 
+            Decoder module implementations to be swapped out without affecting
+            the ProtocolEngine.
+        </para>
+            
+        <para>
+            The interfaces for these Snacc independant request and response
+            objects should be kept within the common subproject so both clients
+            and the server can access them.  Final concrete implementations 
+            should reside within the package holding BER library dependant 
+            classes used to extract these parameters or add them to the BER 
+            library specific envelope.  Within there BER library specific 
+            packages the implemenation objects would expose package friendly 
+            methods to build and modify read-only parameters thereby preserving
+            secure access and modification to critical protocol mandated request
+            and response parameters.  Utility classes used to build or extract 
+            response and request parameters from message envelopes composed of
+            ASN.1 compiler generated Java stub classes, needs not reside within
+            the server subproject.  These BER library dependant clases will be
+            used by both clients and server Encoder/Decoder module 
+            implementations and hence are shared across subprojects.  They are
+            best kept within the common subproject close to the BER library
+            dependant stubs which they operate upon.
+        </para>
+        
+        <para>
+            The design goal to standardize request and response scope 
+            parameter handling leads to several architectural benefits.  The
+            benefits and drawbackes are itemized below:
+        </para>
+        
+        <itemizedlist>
+            <title>Benefits</title>
+        
+            <listitem><para>
+            Centralized acess to request and response parameters.
+            </para></listitem>
+
+            <listitem><para>
+            Standard type explict accessors and mutators of request parameters.
+            </para></listitem>
+
+            <listitem><para>
+            Data encapsulation with secure accessor and mutator visibility.
+            </para></listitem>
+            
+            <listitem><para>
+            Diminished dependence on specific (Snacc) BER libraries used.
+            </para></listitem>
+            
+            <listitem><para>
+            Clear distinction between the role of processors and request
+            or response value objects.
+            </para></listitem>
+            
+            <listitem><para>
+            Greater potential for code reuse across clients and server modules.
+            </para></listitem>
+            
+            <listitem><para>
+            Clear, distinct and isolated facility dedicated to request scope
+            management.
+            </para></listitem>
+            
+            <listitem><para>
+            Generally adds more flexibility to the architecture while making it
+            more coherent.
+            </para></listitem>
+            
+            <listitem><para>
+            Enables potential later for processor pooling rather than 
+            instantiation for each request.  May not really be worth that much
+            since processor creation is not expensive.
+            </para></listitem>
+        </itemizedlist>
+        
+        
+        <itemizedlist>
+            <title>Drawbacks</title>
+            
+            <listitem><para>
+                Requires additional interfaces and classes within the common
+                subproject.
+            </para></listitem>
+            
+            <listitem><para>
+                Requires changes to the ClientModule, EncoderModule, 
+                DecoderModule and the ProtocolModule along with a rewrite of the
+                request processors.  SEDA stage events will also need 
+                modification to use these objects instead of the LDAPMessage
+                wrapper generated by Snacc.
+            </para></listitem>
+            
+            <listitem><para>
+                Adds the runtime time and space overhead of copying parameters 
+                into the agnostic request and response objects.  This will be
+                ameliorated by removing this functionality from the processors.
+            </para></listitem>
+            
+            <listitem><para>
+                More work and more code.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Design</title>
+        
+        <para>
+            An extra package has been created for LDAPv3 operation value object
+            interfaces which represent requests and responses.  We'll begin by
+            defining these interfaces within the package org.apache.ldap.common.ops.  A
+            single package rather than one for responses and another for 
+            requests was chosen to minimize the package import statements of
+            dependent code.
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+        </para>
+
+        <para>
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/rootdse-design.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/rootdse-design.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.3 $</revnumber>
+                <date>$Date: 2003/05/03 01:21:32 $</date>
+                <revdescription>
+                    <para>
+$Log: rootdse-design.xml,v $
+Revision 1.3  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.1  2003/03/10 23:24:22  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.1  2003/03/03 04:53:51  akarasulu
+backend.xml will be backend-module.xml and rootdse-design.xml will have
+stuff in html file moved into it.
+
+Revision 1.1.2.1  2003/03/03 04:49:25  akarasulu
+Added as placeholders for now.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+        </para>
+
+        <para>
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/schema-module.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/schema-module.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,594 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.6 $</revnumber>
+                <date>$Date: 2003/05/03 01:45:23 $</date>
+                <revdescription>
+                    <para>
+$Log: schema-module.xml,v $
+Revision 1.6  2003/05/03 01:45:23  bearcej
+Refix image URLs
+
+Revision 1.5  2003/05/03 01:43:08  bearcej
+Add maven file to help generate server site.
+Modify graphic links again.
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.4  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.3  2003/04/08 02:29:55  bearcej
+* Clean up project.properties
+* Fix logos in project.xml
+* Fix links to image files in docs
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.3  2003/03/15 17:24:14  bearcej
+Fixed figure tags to conform to simple docbook.
+Modified Files:
+ Tag: ALPHA-0_7
+    schema-module.xml
+
+Revision 1.1.2.2  2003/03/10 23:43:33  akarasulu
+Moved links so that image references point to /image/design.  Note that
+references do not have a [.] in front since the maven driven transforms
+automatically append [.] to references.
+
+Revision 1.1.2.1  2003/03/10 23:24:22  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.3  2003/03/09 21:50:26  akarasulu
+Ok the schema documentation is done.
+
+Revision 1.1.2.2  2003/03/09 04:59:15  akarasulu
+Checking in a start to the schema manager documentation.
+
+Revision 1.1.2.1  2003/03/03 04:49:25  akarasulu
+Added as placeholders for now.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+            The purpose of the SchemaModule is to parse the various published 
+            schema files storing their contents in a form suitable for answering
+            schema related questions.  Schema checking, value normalization and 
+            filter processing as well as most fundamental backend operations 
+            require schema knowledge.
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+
+    <section>
+        <title>Architecture</title>
+
+        <section>
+            <title>Brief Overview of Schema Operations</title>
+
+            <para>
+                Schema information is critical within the server for schema 
+                checking, value normalization, searching and syntax checking.  
+                To define how the SchemaModule or any SchemaManager 
+                implementation is to be designed we must first define how these 
+                aspects relate to servicing protocol requests.
+            </para>
+            
+            <para>
+                Schema's define a set of objectclasses, attributes, syntaxes 
+                and matching rules.  Schema checking in general can be defined 
+                as applying constraints imposed by these definitions.  For 
+                example attribute values are constrained with respect to their 
+                syntax and cardinality.  Entries for example will be constrained
+                to include the MUST list of attributes in order to conform to 
+                the objectclasses they implement and so on.  Schema checking is 
+                applied by default.  If you attempt to add unknown attributes to
+                an entry whose objectclasses do not allow, the operation will 
+                not be permitted by the server.  Likewise, an attempt to add a 
+                String valued attribute which is defined to only support numeric
+                values will not be permitted.  This is what we mean by schema 
+                checking.  The server hence uses these definitions to constrain 
+                entries and the values of their attributes, rejecting any
+                operation that would violate the imposed constraints.
+            </para>
+            
+            <para>
+                Value normalization is critical for correctly searching the 
+                directory.  When entries are added to the directory the values 
+                of their attributes are stored and returned in the format they 
+                were provided preserving both whitespace and case.  However when
+                users attempt to search the directory they provide search 
+                filters with attribute value assertions like (cn=James Brown).  
+                The entry that we intend to recall may have had the cn attribute 
+                set as " james   BROWN".  Because the original user provided cn 
+                was entered in a different case with several spaces between the 
+                two parts of the full name, the match will not occur and the 
+                intended entry will not be returned.  Failure to retrieve the 
+                entry would be the case if value normalization were not used.
+                Fortunately, value normalization has been implemented yet it 
+                requires attribute schema definitions for correct operation.  
+                LDAP has defined the concept of a matching rule specifically to 
+                assist in value normalization.  The matching rule for an 
+                attribute defines how values of that attribute are to be 
+                compared.  In the case of the cn example above, the matching 
+                rule defined for the attribute is called caseIgnoreMatch.  With 
+                this knowledge the server generates normalized keys for matching
+                attributes based on the matching rule.  For the original user 
+                provided cn value of " james   BROWN" the server will create a 
+                normalized key of "james brown".  Since the attribute type is
+                case indifferent, its safe to arbitarily (yet consistantly) 
+                generate keys in one case only.  Also notice that several spaces 
+                in between the two names were consolidated into one space and 
+                spaces at either ends of the string were stripped off.  This 
+                type of white space normalization preserves value tokenization 
+                while removing unnecessary whitespace.  We call the operation a 
+                deep trim as opposed to a superficial trim that removes 
+                whitespace at both ends only.  Unlike simple trims which leave 
+                inner stretches of whitespace, a deep trim, condenses inner
+                whitespace regions into a single space.  When another directory 
+                user tries to search the directory specifically looking for the
+                James Brown example entry with the filter (cn=James Brown), the 
+                filter attribute values are automatically normalized based on 
+                the matching rules of schema definitions.  The effective 
+                normalized filter would then be (cn=james brown).  The value of 
+                the cn filter assertion is compared against the normalized user 
+                provided cn value of the entry to produce a match and return the
+                correct candidate entry.  Without the use of matching rules 
+                defined in schema definitions, correct normalization would not 
+                be possible resulting in a highly input sensitive directory 
+                server.
+            </para>
+            
+            <para>
+                Syntax checking is the process of validating correct values for
+                attributes.  Attributes will need to be constrained according to
+                format, type and size.  Syntaxes define the manner in which 
+                attribute values should be checked for validity.  Again these 
+                rules are defined within published schemas to constrain possible 
+                values.  Examples of syntaxes are Boolean whose IANA assigned 
+                public object identifier (OID) is 1.3.6.1.4.1.1466.115.121.1.7.  
+                Boolean attributes can only have values of "TRUE" or "FALSE".  
+                Any other value for attribute types with this syntax will result
+                in a schema violation - the server will reject any operation 
+                resulting in this violation.
+            </para>
+            
+            <para>
+                LDAP searches must take into account attribute inheritance.  
+                When a search with a filter of (name=abc) is conducted it should
+                return all entries with attributes derived from name.  Meaning
+                entries where cn or sn equal "abc" should be returned.  The name
+                attribute is the least specific base for cn and sn which derive
+                from name.  Attribute inheritance trees are defined within the
+                schema.  Without this schema information correct matching cannot 
+                occur as specified by the protocol.
+            </para>
+        </section>
+        
+        <section>
+            <title>The LDAP Schema Dilema</title>
+            
+            <para>
+                LDAP defines the concept of a RootDSE entry.  The RootDSE entry
+                is where several attributes specific to the directory server or
+                Directory Server Agent (DSA) are stored.  The RootDSE stands for
+                the root DSA Specific Entry (DSE).  It holds for example the 
+                location of a set of replica servers that can be contacted in 
+                case this server is unavailable or unable to service requests 
+                due to heavy load.  One of the attributes contained within the 
+                RootDSE is the subschemasubentry attribute.  All entries in an 
+                LDAP server can contain what is known as the subentrysubschema 
+                attribute.  The attribute is intended to point to or refer to a 
+                local entry which stores amongst other things the schema 
+                definitions constraining the refering entry.  The 
+                subschemasubentry attribute is single valued so the RootDSE and
+                any other regular entry can only contain a single value for it.
+                This makes sense since an entry should be governed by only one
+                schema.  If more than one set of schema definitions governed the
+                composition of an entry inconsistant definitions could occur.  
+            </para>
+            
+            <para>
+                The intention of having a subschemasubentry within the RootDSE 
+                was to enable the discovery of the governing directory schema.
+                This implies a one to one relationship between a schema and a
+                directory server.  In fact most LDAPv3 servers manage a single
+                common schema for the entire Directory Information Tree (DIT) of
+                the server.  It is a common practice to use cn=schema for the
+                value of the subschemasubentry.  Novell's NDS and the Netscape 
+                Directory Server (a.k.a. SUN One Directory Server) store schema
+                definitions in this entry with a DN of cn=schema.  Serveral 
+                other commercial servers do the same.
+            </para>
+            
+            <para>
+                This is a very limiting aspect to LDAP.  The explosion of
+                published schemas stuffed into the cn=schema entry has conjested 
+                the entry with attributes for objectclasses, attribute 
+                definitions and syntaxes not to mention ACI attributes that are 
+                also injected into the entry.  Pulling down the massive entry 
+                takes far too long incomparison to the average sized entries in
+                the DIT.   Sure filters can be used to bring down subsets of the
+                entry as needed, however this does not resolve the problem fully
+                since several values for attributes exist and entire definition
+                bodies are embedded within these values as large text objects.  
+                Such selective filters on the entry still need to use substring 
+                matching which is the least efficient and accurate manner for 
+                searching the entry other than using the approximate search
+                operator.  Overall the cn=schema entry is massive with hundreds
+                if not thousands of attributes, and it is very difficult to 
+                search the contents of this entry efficiently and accurately to 
+                answer schema related questions.
+            </para>
+            
+            <para>
+                Another limitation imposed by storing all schema information in
+                one place is the implication that only one schema governs the
+                composition of all entries within the directory.  The need to
+                constrain different regions of the tree through the definition
+                of alternative schemas will inevitably arise.  It is very 
+                unreasonable to consolidate and limit all schema definitions to
+                one.  What's the point of having the potential for every entry 
+                to contain a subschemasubentry attribute?  Why enable the 
+                attribute in every entry if there can only be one schema defined
+                for the entire DIT?  By convention, the entry referred to by the
+                DN value stored in the RootDSE's subschemasubentry attribute 
+                could be implied as the one place for the storage of schema 
+                definitions, since only one schema governs the entire directory.
+                This contradiction leads us to believe that the protocol at its
+                inception borrowed too little from its more robust parent, the
+                X.500 directory.  The subschemasubentry concept made its way 
+                into LDAP but not fully.  This is why LDAP servers do not 
+                provide the ability to manage multiple regions governed by 
+                different schema definations.  They do however provide the 
+                ability to have different entries point to multiple schemas by 
+                allowing all to have a subschemasubentry attribute.
+            </para>
+            
+            <para>
+                LDAP never clearly defined a means to managing schema data in 
+                separate regions of the directory tree - this was left up to the
+                server implementation if supported at all.  The notion of 
+                partitioning the directory tree into regions managed by 
+                different schemas was very explicitly defined by the X.500 
+                standard in what they called Subschema Authoritative Areas or
+                SAAs.  These regions were defined at various points in the 
+                DIT to mandate schema constraints.  The coverage of the area
+                was defined using a subtree specification and SAAs could be 
+                located anywhere and occupy any subtree shape imaginable based
+                on the parameters of the specification.  Unlike X.500 LDAP lacks
+                these rich constructs.
+            </para>
+            
+            <para>
+                While designing the SchemaManager service we kept these short
+                falls in mind with the intention of alleviating them if not 
+                irradicating them.  The need to enable multiple schemas 
+                restricted to specific regions of the DIT was too compelling to
+                ignore.  At a bare minimum we wanted to enable different schemas
+                for DIT partitions or in otherwords backends of the server while
+                remaining protocol compliant giving users the same environment
+                they are accostomed to.  Our attempt to do so is incomplete and
+                an ongoing process.  Before the protocol compliant alpha 
+                release we would like to enable a super schema entry at 
+                cn=schema for default schema support in the expected fashion. We
+                would also like to enable each backend to have its own schema
+                for the time being until a more complete mechanism for schema
+                management can be established.  While stuck in this limbo, we
+                can investigate further constructs and mechanisms within X.500
+                which can be incorporated into the server while remaining in 
+                full compliance with the LDAPv3 specifications.  The design 
+                described for now regarding the SchemaManager and its associated
+                helper interfaces is mostly an intermediate design.
+            </para>
+                
+            <para>
+                Rather than implement full blown SAAs we decided to borrow a
+                little from them.  We simply assigned an SAA to a DN and 
+                considered the subtree of the SAA to encompass everything below
+                it.  In the end we fell back on just requiring an SAA to be 
+                defined for every naming context or backend suffix to be 
+                attached to the server.  Hence every backend in the server must 
+                have its own set of schema definitions.  Was this the correct 
+                way to proceed?  Probably not!  But it was done and for the time 
+                being it will stay until we refactor the design into a more 
+                accurate one.  Also note that for the time being no schema entry
+                referred to by the subschemasubentry exists at the present time,
+                so even though schemas are applied one for each backend, they 
+                nor the superset of them are published to the outside world.
+                With these facts noted we shall continue in the next section to
+                describe the interfaces within the schema package.
+            </para>
+        </section>
+        
+        <section>
+            <title>
+                SchemaManager Service Interface And Its Associated Helpers
+            </title>
+            
+            <para>
+                The SchemaModule implements the SchemaManager service interface
+                which declares two methods to access a Schema object: 
+                getSchema() and getCompleteSchema().  The getSchema() method,
+                requires a String argument specifying a distinguished name.  As
+                a consequence of having implementations possibly parse the 
+                String DN argument into a Name, getSchema() throws a 
+                NamingException.  As can be inferred getSchema() retrieves the 
+                Schema instance in effect at a point (or entry) within the 
+                directory specified by DN.  The returned schema is the schema
+                defined by the SAA at that suffix DN.  The other method, 
+                getCompleteSchema() retrieves the union of all the schema object 
+                definitions within the entire directory as one Schema instance.
+                The returned Schema representing the superset of schema 
+                definitions would be equivalent to the content stored by a
+                cn=schema entry if one existed.
+            </para>
+            
+            <para>
+                The role of the SchemaManager service is simple.  It is a 
+                location specific Schema factory.  The Schema interface is the
+                contract through which answers to schema related questions are
+                obtained.
+            </para>
+            
+            <para>
+                Every aspect with regard to the effective schema constraining
+                a point in the directory can be assertained by methods on the 
+                Schema interface.  These methods enable most of the schema 
+                operations required for syntax checking, attribute value 
+                normalization, and schema checking to detect object class 
+                violations.  Access to other interfaces are provided to perform
+                these operations via the Schema.
+            </para>
+            
+            <para>
+                The Schema interface enables access to two types of NameParsers
+                used to parse a String representation of a DN into a Name.  The 
+                getNameParser() method returns a NameParser that does not 
+                normalize name component attribute identifiers or their values.
+                The getNormalizingParser() method returns a NameParser that does
+                normalize attribute identifiers and values based on the 
+                attribute definitions in the Schema instance returning the 
+                parser.  Note that for each name component attribute value pair 
+                within a distinguished name a different normalization function
+                may need to be applied.  The normalizing name parser must lookup
+                the normalizer to use for each of the component attributes.  Of 
+                course this information is available within the Schema that 
+                creates the normalizing name parser.
+            </para>
+            
+            <para>
+                Access is also provided to an LdifParser and an LdifComposer.  
+                These interfaces define methods for taking a MultiMap to and
+                from an LDIF representation.  A MultiMap is a extention on the
+                Map interface to allow a single key to have multiple values 
+                defined for it.  It is ideal for storing attribute values since 
+                attributes can have a single value or have multiple values.  The
+                MultiMap interface is defined within the Apache Commons 
+                Collections package.
+            </para>
+            
+            <para>
+                Another interface accessible through the Schema.getNormalizer()
+                method is Normalizer.  Normalizers are attribute specific.  They
+                transform the values of String attributes according to the 
+                matching rules defined for them.  For example some default 
+                normalizers for case insensitive Strings attributes are defined 
+                to do a deepTrimToLower and others that are for case sensitive 
+                Strings may perform a deepTrim without transforming alpha 
+                characters into lower case.
+            </para>
+            
+            <para>
+                The Schema interface itself defines several other methods that 
+                test, or transform values of a specific attribute type.  Another 
+                Schema method is provided to apply a syntax check on an entire 
+                entry.
+            </para>
+        </section>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <section>
+            <title>Schema Files</title>
+            
+            <para>
+                Presently, schema definitions are stored in schema files.  These
+                static ascii files use the standard <ulink url=
+                "http://ldap.akbkhome.com/attribute/attributeTypes.html"> 
+                Attribute Type Description </ulink> and <ulink url=
+                "http://ldap.akbkhome.com/attribute/objectClasses.html">Object 
+                Class Description</ulink> syntax as specified within <ulink url=
+                "http://www.faqs.org/rfcs/rfc2252.html"> RFC2252</ulink>.  They
+                are slightly modified to facilitate parsing ease by prefixing
+                the descriptions with either a 'attributetype' or 'objectclass' 
+                marker.  The format as well as the various files used were 
+                adopted from the definitions managed by the <ulink url=
+                "http://openldap.org">OpenLdap</ulink> effort.
+            </para>
+            
+            <para>
+                The definitations are categorically separated into separate 
+                files one for each major group of definitions.  For example the
+                core set of definitions in RFC2252 are resident in a schema file
+                named core.schema.  Other popular and published type 
+                descriptions exist like the java schema and the corba support
+                schema.  The contents of these files are parsed and stored in 
+                AttributeSpec and ObjectClassSpec beans.
+            </para>
+        </section>
+        
+        <section>
+            <title>Initialization Time</title>
+            
+            <para>
+                The SchemaModule creates various Schema instances as a 
+                consequence of initialization.  These Schema objects are 
+                accessible using a DN. Their contents are based on the 
+                contents of the parsed schema files and the SchemaManaqer's 
+                configuration section within the config.xml file.  The 
+                configuration for the SchemaManager specifies the set of schema
+                files that can be used to define SAAs.  The files are referred 
+                to within schema elements which assigns a schema name to the
+                file.  At configuration time the very first task performed is
+                the parse of these files.  For each file a Schema object is 
+                populated with AttributeSpec and ObjectClassSpec objects.  While
+                filling the target Schema object, a superset Schema object 
+                returned using the getCompleteSchema() method, is populated as
+                well.  The complete schema not only fulfills the needs for a 
+                complete schema hangling off of the cn=schema entry, but also
+                allows for cross referencing between schema files.  Sometimes
+                an attribute or objectclass is defined using existing core 
+                schema attributes and objectclasses.  Resolution of values using
+                parent objectclasses and attributes is made possible by this
+                complete union schema of all schema definitions stored accross
+                these files.
+            </para>
+        
+            <para>
+                Once all schema files are parsed a normalization configuration
+                element section is used to construct Normalizers for the various
+                matching rules the server must implement.  Next a configuration
+                section for user defined syntax checkers is used to create 
+                syntax mandating elements used by the schema to check attribute
+                values for correct syntax usage.  Finally once all schema 
+                objects have been populated with their specification beans, 
+                required normalizers and syntax checkers the SAAs of the 
+                configuration are created.
+            </para>
+            
+            <para>
+                SAA elements define the set of schema object that are valid 
+                below and at a point within the directory.  Every descendent
+                entry at and under the SAA DN will be constrained by the set
+                of schema objects defined.  The SAA element contains a set of
+                references to the schema files parsed.  For each SAA element a
+                new Schema implementation is instantiated and populated with the
+                AttributeSpec beans, ObjectClassSpec beans, Normalizers and 
+                syntax checkers of the reference Schema objects.  Hence an SAA
+                is just another Schema that is the union of several schema file
+                Schemas.
+            </para>
+        </section>
+        
+        <section>
+            <title>Runtime</title>
+            
+            <para>
+                At runtime several Maps within the SchemaImpl instances are 
+                already populated.  These set of Maps are used to do fast 
+                lookups to access AttributeSpecs,  ObjectClassSpecs, Normalizers
+                and SyntaxCheckers.  The various methods on the Schema interface
+                are implemented using these Maps within the SchemaImpl class.
+            </para>
+        </section>
+        
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+            Eventually we would like to solve the schema dilema for LDAP 
+            elegantly without breaking with protocol requirements.  No matter
+            what the same referrence to the superset of schema elements must be
+            maintained within the entry referred to by the subschemasubentry 
+            attribute in the RootDSE.  At the present moment we have delayed the
+            implementation of this schema administration entry.  It must at some
+            point be implemented to comply with the standard mode of schema 
+            inquire to be familiar to users.  However nothing is stopping us 
+            form making this entry the suffix to an entire backend under which 
+            other entries can reside to yeild specific information in a 
+            searchable format concerning schema objects and SAAs.  For example 
+            if we were to use cn=schema as the value for the subschemasubentry
+            attribute within the RootDSE, then cn=schema can be the suffix of 
+            a backend with entries below.
+        </para>
+
+        <para>
+            At the present time we do not have a clear understanding of exactly
+            how we intend to structure this tree.  We do know that we want to 
+            enable the decomposition of attribute type and objectclass 
+            descriptions into searchable entries.  A special schema can be 
+            defined to store schema information itself.  This way the search 
+            machinery of the backend can be used to answer complicated questions
+            regarding attribute and objectclass relationships in a standard way.
+            Otherwise we will have to devise Maps within the Schema 
+            implementation to correlate descriptions with one another.  We would
+            also like to search descriptions and SAA specifications without 
+            requiring clients to parse their syntactic representation.  Breaking
+            these specifications down into entries solves these problems.  While 
+            supporting these extra features to aid in schema support we would 
+            like to provide the expected constructs for backwards compatability.
+            Eventually we hope that we can persuade the LDAP community to see 
+            the value proposition of schema descriptions specified as entries
+            with a deep schema administration context rooted at cn=schema or 
+            what ever is specified by the subschemasubentry within the RootDSE.
+        </para>
+        
+        <para>
+            Just for fun we envisioned the following DIT structure to a schema
+            administration context.  The diagram below represents the DIT.
+            Imagine for a moment that cn=schema were the root of the schema
+            administration naming context.  Under it would reside containers
+            for entries describing attribute types, objectclasses, syntaxes,
+            and matching rules.  Also located under cn=schema could reside SAA
+            specification describing the subtree of authority and a schema 
+            definition set of the schema objects composing the SAA.  Questions
+            as to which SAA governs the schema of an entry can be used in a 
+            filter to search for the appropriate SAA in charge.  Hence the 
+            effective subschemasubentry value if asked for of an entry can be
+            extracted at entry retrival time by searching the ou=SAA branch.
+        </para>
+        
+        <figure>
+            <title>Speculation on a Administrative Schema DIT Structure</title>
+            <graphic fileref="../images/AdminSchemaContext.gif"/>
+        </figure>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+            Incomplete since the RootDSE subschemasubentry attribute referral to
+            a schema description entry does not exist.  We need a cn=schema 
+            entry to present attributeType and objectClasses attributes in the
+            proper expected syntax.
+        </para>
+        
+        <para>
+            Messy adhoc implementation with maps all over the place.
+        </para>
+        
+        <para>
+            The inheritance thing does not work and there are a number of 
+            bugs where cross schema references do not resolve.
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/seda-implementation.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/seda-implementation.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Module Implementation</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.3 $</revnumber>
+                <date>$Date: 2003/05/03 01:21:32 $</date>
+                <revdescription>
+                    <para>
+$Log: seda-implementation.xml,v $
+Revision 1.3  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.1  2003/03/10 23:24:22  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.1  2003/03/03 04:49:25  akarasulu
+Added as placeholders for now.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+        <para>
+        </para>
+    </abstract>
+    
+    <section>
+        <title>Document TODOs:</title>
+        <itemizedlist>
+            <listitem><para>
+                Add diagrams.
+            </para></listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Implementation</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+    <section>
+        <title>Future</title>
+        
+        <para>
+        </para>
+
+        <para>
+        </para>
+    </section>
+
+    <section>
+        <title>Faults</title>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+        
+        <para>
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/server-architecture.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/server-architecture.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,613 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+<article class="whitepaper">
+    <title>Server Architecture</title>
+    
+    <articleinfo>
+        <author><othername>akarasulu</othername></author>
+        <editor><othername>$Author: bearcej $</othername></editor>
+        <revhistory>
+            <revision>
+                <revnumber>$Revision: 1.4 $</revnumber>
+                <date>$Date: 2003/05/03 01:21:32 $</date>
+                <revdescription>
+                    <para>
+$Log: server-architecture.xml,v $
+Revision 1.4  2003/05/03 01:21:32  bearcej
+Remove tabs from doc files and fix linefeeds.
+
+Revision 1.3  2003/04/21 21:11:50  bearcej
+
+* Fix broken links in documentation.
+
+Revision 1.2  2003/03/23 13:24:46  akarasulu
+Added these files from the ALPHA-0_7 branch.
+
+Revision 1.1.2.3  2003/03/15 17:21:58  bearcej
+Fixed abstract tag.
+Modified Files:
+ Tag: ALPHA-0_7
+    server-architecture.xml
+
+Revision 1.1.2.2  2003/03/10 23:43:33  akarasulu
+Moved links so that image references point to /image/design.  Note that
+references do not have a [.] in front since the maven driven transforms
+automatically append [.] to references.
+
+Revision 1.1.2.1  2003/03/10 23:24:22  akarasulu
+Moved design documentation from docs/design to src/docbook/design.
+
+Revision 1.1.2.9  2003/03/02 00:37:32  akarasulu
+Adjusting spacing.
+
+Revision 1.1.2.8  2003/03/02 00:32:39  akarasulu
+Completed listener documentation but needs some work and added the article
+info tag to both these document.  Also flaged these docs as whitepapers.
+
+                    </para>
+                </revdescription>
+            </revision>
+        </revhistory>
+    </articleinfo>
+
+    <abstract>
+    <para>
+        The server consists of a set of loosely coupled modules.  All server
+        modules are not created equally.  Some modules are stages that take
+        part in the Staged Event Driven Architecture (SEDA) aspect of the 
+        LDAPd server.  Some modules are startable, having a single driver thread.
+        The simplest modules have no driver at all relying on the threads 
+        of caller modules to drive their workload.  This document describes the
+        modular structure of the server without delving deep into each specific 
+        module, but rather defining what a module is, and the various types of 
+        modules within the server.  Other documents are dedicated to the 
+        implementation of each specific module.
+    </para>
+    </abstract>
+    
+    <section>
+        <title>What is a Module?</title>
+        
+        <para>
+            LDAPd is composed of modules, but what exactly is a module?  The
+            definition is partly defined by the LDAPd project and partly defined
+            by the Avalon Framework.  Avalon defines lifecycle methods for 
+            components that fit into a framework.  Components define interfaces
+            used to manage lifecycle operations.  These lifecycle aspects are 
+            then automatically managed by a component conatiner using the 
+            Inversion of Control Pattern.  Inversion of Control enables the
+            construction of reusable loosely coupled components.  Avalon 
+            Framework specifies the various lifecycle methods using interfaces
+            that components may have.  The framework is just that, an API mostly
+            composed of interfaces.  The designers of Avalon have thought of 
+            all the possible generalized lifecycle methods, components may have
+            and have forged a set of interfaces for component designers to use.
+        </para>
+        
+        <para>
+            The concept of an LDAPd module is defined in terms of Avalon 
+            Framework interfaces.  In terms of lifecycle behavoirs, an LDAPd 
+            module is a log enabled, configurable, initializable, startable, 
+            and coincidentally, stopable, thread safe component with potential 
+            dependencies on other components within the server.  The Avalon 
+            Framework interfaces and their methods define these lifecycle 
+            behavoirs.  We have programatically defined an LDAPd module as an 
+            interface that extends many Avalon Framework interfaces.  Not 
+            surprisingly, the name of the interface is Module and it is located
+            in the org.apache.eve package.  All LDAPd modules must implement this 
+            interface.  The Module interface simply states the lifecycle methods
+            a module must implement.  The set of Avalon Framework interface's 
+            extended by the Module interface are listed below:
+        </para>
+
+        <itemizedlist>
+            <listitem><para>Startable</para></listitem>
+            <listitem><para>ThreadSafe</para></listitem>
+            <listitem><para>LogEnabled</para></listitem>
+            <listitem><para>Serviceable</para></listitem>
+            <listitem><para>Configurable</para></listitem>
+            <listitem><para>Contextualizable</para></listitem>
+            <listitem><para>Initializable</para></listitem>
+        </itemizedlist>
+        
+        <para>
+            Besides mandating the lifecycle functions, the Module interface 
+            also defines extra methods used to query information regarding the 
+            role, name and implementation of a Module.  More than one module 
+            implementation can exist for a module service type, and the specific
+            implementation chosen can be delayed until runtime.  Besides the
+            Module interface, a base AbstractModule class is defined as an 
+            adapter which correctly handles most of the lifecycle methods of a
+            module.  Subclasses of AbstractModule are expected to implement 
+            some criticle and highly specific interface methods to be concrete.
+        </para>
+    </section>
+
+    <section>
+        <title>Core Service Interfaces</title>
+        
+        <para>
+            Building a module for the sake of implementing the required 
+            lifecycle interfaces does not make sense.  A module needs to do 
+            something by providing a service.  The Avalon Framework defines the 
+            concept of a service interface for components.  Publicly exposed
+            service interfaces provide methods that callers invoke to interact 
+            with the service.  The service interface in this way defines the 
+            role of the module within the server.  The service interfaces are 
+            completely decoupled and independant of the lifecycle methods.
+        </para>
+        
+        <para>
+            LDAPd defines a set of core service interfaces which must be 
+            implemented as server modules.  These are the set of manditory 
+            services required to make the server work.  These services are
+            listed below:
+        </para>
+
+        <table frame='all'><title>Mandarory LDAPd Services</title>
+            <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+                <colspec colname='Interface'/>
+                <colspec colname='Package'/>
+                <colspec colname='Description'/>
+                <thead>
+                    <row>
+                        <entry>Service Interface</entry>
+                        <entry>Java Package</entry>
+                        <entry>Service Description</entry>
+                    </row>
+                </thead>
+                <tbody>
+                    <row>
+                        <entry>UnifiedBackend</entry>
+                        <entry>org.apache.eve.backend</entry>
+                        <entry>The junction where all backends connect.</entry>
+                    </row>
+                    <row>
+                        <entry>ClientManager</entry>
+                        <entry>org.apache.eve.client</entry>
+                        <entry>Maintains and tracks client sessions.</entry>
+                    </row>
+                    <row>
+                        <entry>Decoder</entry>
+                        <entry>org.apache.eve.decoder</entry>
+                        <entry>Demarshals ASN.1 BER LDAPv3 messages</entry>
+                    </row>
+                    <row>
+                        <entry>Encoder</entry>
+                        <entry>org.apache.eve.encoder</entry>
+                        <entry>Marshals ASN.1 BER LDAPv3 messages</entry>
+                    </row>
+                    <row>
+                        <entry>EventManager</entry>
+                        <entry>org.apache.eve.event.protocol</entry>
+                        <entry>
+                            Central point for firing and recieving protocol 
+                            events - not related to stage events.
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>InputManager</entry>
+                        <entry>org.apache.eve.input</entry>
+                        <entry>
+                            Detects input on client channels to invoke the 
+                            decoder.
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>Encoder</entry>
+                        <entry>org.apache.eve.encoder</entry>
+                        <entry>Marshals ASN.1 BER LDAPv3 messages</entry>
+                    </row>
+                    <row>
+                        <entry>JndiProvider</entry>
+                        <entry>org.apache.eve.jndi</entry>
+                        <entry>
+                            Factory for internal serverside JNDI LdapContexts
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>ServerListener</entry>
+                        <entry>org.apache.eve.listener</entry>
+                        <entry>
+                            Detects initial connections between the client and 
+                            the server.
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>OutputManager</entry>
+                        <entry>org.apache.eve.output</entry>
+                        <entry>
+                            Handles response PDU buffer streaming to the client.
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>ProtocolEngine</entry>
+                        <entry>org.apache.eve.protocol</entry>
+                        <entry>
+                            The heart of the server which processes protocol
+                            requests to generate the protocol responses if 
+                            required.
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>SchemaManager</entry>
+                        <entry>org.apache.eve.schema</entry>
+                        <entry>
+                            Maintains and stores schema related information
+                            as specified in the config.xml and the various 
+                            schema files associated with subschema authoritative
+                            areas.
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>AuthenticationManager</entry>
+                        <entry>org.apache.eve.security.auth</entry>
+                        <entry>
+                            Manages simple authentication on LDAP bind 
+                            operations both for admin and non-admin users.
+                        </entry>
+                    </row>
+                </tbody>
+            </tgroup>
+        </table>
+    </section>
+    
+    <section>
+        <title>Staged, Executable and Simple Modules</title>
+        
+        <para>
+            As we mentioned before, not all modules are created equal.  Some
+            modules participate in the SEDA aspect of the server.  These modules
+            are stages and must in addition to the Module interface implement 
+            the Stage interface or extend a base class in the org.apache.eve.seda 
+            package.
+        </para>
+        
+        <para>
+            Without replicating Matt Welsh's paper on SEDA we'll consisely 
+            define a stage as an event queue, a driver thread and a worker
+            thread pool.  Stages recieve events which are popped off their 
+            event queues and handed off to a worker thread for processing.  
+            Events this way are processed in parallel and asynchronously.  In
+            SEDA architectures, stages compose event processing pipelines that
+            often out perform their strictly threaded or strictly event driven
+            counterparts.
+        </para>
+        
+        <para>
+            Stages in LDAPd are modeled exactly as defined above.  They have an 
+            event queue, a driver thread and a worker thread pool.  Within the
+            LDAPd server the following core service interfaces are presently 
+            implemented as stages composing the request processing pipeline:
+        </para>
+        
+        <itemizedlist>
+            <listitem><para>ClientManager</para></listitem>
+            <listitem><para>OutputManager</para></listitem>
+            <listitem><para>Encoder</para></listitem>
+            <listitem><para>Decoder</para></listitem>
+            <listitem><para>ProtocolEngine</para></listitem>
+        </itemizedlist>
+
+        <para>
+            These service interfaces may not remain stages for long.  Presently
+            the new IO (java.nio) functionality for nonblocking IO has not been 
+            implemented.  Once implemented stages may split or fuse together. 
+            Some modules may remain but may no longer become stages.  This is 
+            dependent on how the architecture progresses.  What is certain is 
+            that stages will be present as the components of the mature server.
+        </para>
+        
+        <para>
+            Simple modules, that are not stages also exist.  The following 
+            service interfaces are implemented as simple modules without the use
+            of any threads other than the thread of the caller to do work:
+        </para>
+        
+        <itemizedlist>
+            <listitem><para>AtomicBackend</para></listitem>
+            <listitem><para>UnifiedBackend</para></listitem>
+            <listitem><para>EventManager</para></listitem>
+            <listitem><para>JndiProvider</para></listitem>
+            <listitem><para>SchemaManager</para></listitem>
+            <listitem><para>AuthenticationManager</para></listitem>
+        </itemizedlist>
+
+        <para>
+            Some service interfaces are not implemented as stages, or as simple
+            modules.  These implementations use one or more threads to perform
+            some work outside of the thread of any caller.  There are two such
+            service interfaces.  First the InputManager, which detects IO on 
+            client connections uses a pool of threads to listen for client 
+            input stream activity.  This is a temporary work around until the
+            module is implemented using selectable channels within the new IO
+            packages of the JDK 1.4+ platforms.  Once channels and channel 
+            selection is used to implement nonblocking IO, one single thread 
+            can be used by this module to detect IO on any number of clients
+            concurrently.  This is where and when the true power of SEDA will
+            reviel itself, until then we use this classic implementation.  
+        </para>
+        <para>
+            The other service interface which is implemented neither as a stage 
+            nor a simple module, is the ServerListener module.  Currently the 
+            server listener uses a single thread to listen to a single port for 
+            incomming connections.  Presently it cannot listen to more than one
+            port.  It can be extended to listen to more ports by either using
+            non-blocking selectors on multiple server socket's bound to more 
+            than one port, or it can use one pooled thread per server socket.
+            Nevertheless both these service interface implementations will 
+            change, without changing the interface itself, to reduce the number
+            of threads used while increasing concurrency.  These modules may also
+            fuse together using a single channel selector for both client accept 
+            and input IO events.
+        </para>
+    </section>
+    
+    <section>
+        <title>Service Interfaces and Modules</title>
+        
+        <para>
+            Every core service interface defined is implemented as a server 
+            module whether it be as a simple module, a staged module or other.  
+            Each interface has a single implementation instance at runtime with 
+            the exception of AtomicBackends.  Several AtomicBackend 
+            implementations may exist for various entry backing stores.  
+            Currently, there are three different AtomicBackend types: the 
+            modjdbm, modjdbc and the BerkeleyDB backend.  Multiple backend
+            instances of the same type or of different types can coexist in the
+            same server process instance.  Each AtomicBackend instance is only 
+            required to have a unique suffix DN to differentiate itself from 
+            other backends.  One or more AtomicBackend instances hang off of the 
+            UnifiedBackend service which is implemented by the NexusModule.  
+            Below we list the various service interfaces and their corresponding 
+            Module implementations excluding the AtomicBackend implementations:
+        </para>
+        
+        <itemizedlist>
+            <listitem>
+                <para>NexusModule implements UnifiedBackend</para>
+            </listitem>
+            <listitem>
+                <para>ClientModule implements ClientManager</para>
+            </listitem>
+            <listitem>
+                <para>DecoderModule implements Decoder</para>
+            </listitem>
+            <listitem>
+                <para>EncoderModule implements Encoder</para>
+            </listitem>
+            <listitem>
+                <para>EventModule implements EventManager</para>
+            </listitem>
+            <listitem>
+                <para>InputModule implements InputManager</para>
+            </listitem>
+            <listitem>
+                <para>OutputModule implements OutputManager</para>
+            </listitem>
+            <listitem>
+                <para>JndiProviderModule implements JndiProvider</para>
+            </listitem>
+            <listitem>
+                <para>ListenerModule implements ServerListener</para>
+            </listitem>
+            <listitem>
+                <para>ProtocolModule implements ProtocolEngine</para>
+            </listitem>
+            <listitem>
+                <para>SchemaModule implements SchemaManager</para>
+            </listitem>
+            <listitem>
+                <para>AuthenticationModule implements AuthenticationManager
+                </para>
+            </listitem>
+        </itemizedlist>
+    </section>
+    
+    <section>
+        <title>Request Processing</title>
+        
+        <para>
+            Without using a highly detailed sequence diagram we shall represent 
+            at a very high level the flow of SEDA based events within the system 
+            while processing requests that have a single response. 
+            <ulink url="images/ReqRespStageFlow.gif">Figure I</ulink> 
+            is a very high
+            level stage to stage processing sequence for requests that have a
+            single response.  LDAPv3 abandon and unbind requests have no 
+            responses and LDAPv3 search requests may have one or more responses
+            so these requests are not entirely represented in this diagram.  
+            Keep in mind that this sequence diagram trivializes the staged 
+            processing by only showing the stage modules and the events they
+            generate within the pipline.  Other modules that are used indirectly
+            while servicing these requests are not shown.  More detail 
+            concerning module interactions downstream of the ProtocolEngine are
+            provided in the protocol engine's implementation documentation where
+            each request processor implementation is outlined.
+        </para>
+
+        <para>
+            Briefly, Abandon and Unbind requests never produce a response and 
+            Search requests have the potential of generating more than one 
+            response.  Reponseless requests simply stop processing after the 
+            respective request processor completes processing within the 
+            ProtocolModule. The Search request is more complex requiring 
+            synchronized delivery of messages in a specific order.  Special 
+            methods are used to bypass staged event handling both in the 
+            EncoderModule and in the OutputModule when search requests with more 
+            than one response are processed.  Serial delivery of entry responses 
+            in a search requires special synchronous pathways through the 
+            Encoder and Output modules.  The last search done reponse, however 
+            goes through the standard staged event handling mechanism.  Again,
+            Search, Abandon and Unbind LDAPv3 request processing is discussed 
+            in more detail within the low level implementation document for the
+            ProtocolModule.
+        </para>
+
+        <section>
+            <title>Input Detection Stage</title>
+            <para>
+                In the solid state, when all modules have started up, and after 
+                a client has established a connection, an input handler assigned 
+                to every client connection, blocks waiting for IO activity on 
+                the input stream of the client socket.  Once incomming data is 
+                detected an InputEvent is generated and enqueued on the event 
+                queue of the DecoderModule.
+            </para>
+        </section>
+        <section>
+            <title>
+                Protocol Data Unit (PDU) Decoding Stage (PDU Demarshaling)
+            </title>
+            <para>
+                Once the InputEvent is dequeued and processed a decoding occurs 
+                while reading the input stream to build a LDAP request message 
+                envelope.  This stage demarshals the incoming PDU on the client 
+                input stream using Binary Encoding Rules (BER).  Once the 
+                demarshaling is complete a RequestEvent is assembled with the 
+                demarshalled request PDU and enqueued onto the ProtocolModule 
+                stage for handling and response generation.
+            </para>
+        </section>
+        <section>
+            <title>Request Protocol Handling Stage</title>
+            <para>
+                Once the RequestEvent is dequeued by the ProtocolModule's driver
+                thread a protocol request processor is created to process the 
+                request PDU.  A stage worker pool thread is used to drive the 
+                processing.  Depending on the nature of the protocol request 
+                various modules downstream from protocol engine may be directly 
+                or indirectly called upon to complete the request.  Most of the 
+                time the NexusModule is called which routes requests to the 
+                appropriate target backend based on the backend suffix.  Some 
+                protocol requests are special in that they do not return a 
+                response or potentially return more than one response.  Such
+                protocol request types are refered to specifically in the 
+                protocol engine documentation.  For the majority of request 
+                types the processors generate a response message envelope to 
+                be delivered back to the client on the socket output stream.
+                The protocol engine can differentiate between those request 
+                types that generate a response and those that do not.  After, 
+                and if, a response is generated, it is packaged within a newly
+                created ResponseEvent.  This event is enqueued onto the Encoder
+                stage's event queue.
+            </para>
+        </section>
+        <section>
+            <title>Response Encoding Stage (PDU Marshaling)</title>
+            <para>
+                Once the ResponseEvent is popped off of the event queue by the
+                driver thread of the EncoderModule, the packaged response 
+                message envelope (LDAPMessage instance) is accessed.  The object
+                is marshalled into a BER encoded buffer stream.  A input stream
+                to this buffer is created and packaged within a OutputEvent 
+                which is then enqueued on the event queue of the OutputModule
+                stage.
+            </para>
+        </section>
+        <section>
+            <title>Output Handling Stage</title>
+            <para>
+                Once the OutputEvent is popped off of the event queue by the
+                driver thread of the OutputModule, the encoded response PDU can 
+                be pumped from the buffer into the OutputStream of the client.
+                Synchronized access to the OutputStream of the client is 
+                acquired from the ClientModule using the ClientKey packaged in
+                the OutputEvent.  A worker thread form the stage's worker thread
+                pool is assigned to pump the bytes stored in the BER encoded 
+                buffer accessed via an InputStream into the OutputStream of 
+                the client.  This is not an optimal implementation but it works 
+                for now allowing for full system integration testing.  
+                Optimization efforts specifically targeting the JDK1.4 platform
+                (using non-blocking selectors and fast direct memory buffers) 
+                will be underway within the first beta releases.
+            </para>
+        </section>
+    </section>
+
+    <section>
+        <title>Module Interdependence</title>
+        
+        <para>
+            Modules within the server indirectly depend on one another since 
+            they must call each other's service interface methods to process 
+            client requests.  Modules depend on service interfaces (ROLE's), not 
+            on module's implementing those interfaces.  This keeps the modules
+            decoupled and is a motif brought about through the use of Avalon.
+            Cyclic dependencies can however create a chicken and egg problem for 
+            the server kernel (container) used to fire up the server's modules.  
+        </para>
+        
+        <para>
+            The LDAPd server avoids these cyclic dependencies by designating the 
+            ClientModule as a master module and those that depend on it in a 
+            cycle as ClientManagerSlaves.  Many modules need to access client
+            session information managed by the ClientModule.  The ClientModule
+            however passes on requests to the InputModule, which later passes
+            on requests to the ProtocolModule and so on until the OutputModule.  
+            If any of these modules downstream of the ClientModule need to 
+            access the client's session or request its termination they need to
+            make calls to the ClientModule's service interface.  This imposes a
+            cyclic dependency when standard container mechanisms are used 
+            to get handles on the services an interface modules depend upon.  In 
+            Avalon Framework, the Serviceable interface (required by all LDAPd
+            Modules) has the service method, which is the standard mechanism for
+            getting a handle on another service.  When containers detect cycles
+            by monitoring the service lookups made by a Serviceable component,
+            they complain and reject initialization.  Our little workaround for
+            these unavoidable cyclic dependencies is to, in some respects, 
+            violate, yet formalize a registration mechanism outside of the 
+            Serviceable paradigm, thereby bypassing cycle detection in some 
+            pathological cases.
+        </para>
+        
+        <para>
+            Upon recommendations from the Avalon team we constructed a special
+            ClientManagerSlave interface.  This interface has a single method
+            which is used to register the ClientModule (a.k.a. the 
+            ClientManager).  By passing the handle to the ClientManager using 
+            ClientModule initiated registration, modules dependent on the 
+            ClientManager service need not raise cyclic dependency errors.  
+            Regardless cyclic references exists but because the Serviceable 
+            interface is not used to grab the handle containers don't choke.  
+            This is why the InputManager, OutputManager and the ProtocolEngine 
+            interfaces extend the ClientManagerSlave interface.  On 
+            initialization the ClientManager registers itself with the 
+            ClientManagerSlaves so they need not lookup the ClientManager within
+            the service lifecycle method.
+        </para>
+        
+        <para>
+            <ulink url="images/ModuleDependencies.gif">Figure II</ulink>
+            is a component
+            diagram which depicts the service interfaces and the modules that
+            implement them.  The components are modules that have a solid 
+            supporting link to a circular service interface node.  Note that
+            the modules are service interface implementations and they have 
+            directed dotted line dependencies on the service interfaces 
+            supported by other modules.  Modules never directly depend on other
+            modules, nor should they ever!  
+        </para>
+    </section>
+    
+    <section>
+        <title>Conclusion</title>
+        <para>
+            Our goals in this article were to introduce the fundimental modular
+            architecture of the server.  We want readers to be able to 
+            comprehend how server modules are defined according to the Avalon
+            Framework APIs.  Readers should therefore understand what a module 
+            is as a Avalon Component and which service interface is supported
+            by each critical server module.  We would also like readers to step 
+            away with a rough understanding of the SEDA stages within the server
+            and to be able to differentiate between simple modules, stages and 
+            those that fall under neither category.  Ultimately users should 
+            possess an understanding of module dependencies and their 
+            interactions within the processing pipeline for LDAP requests with 
+            the subsequent transmission of a response.
+        </para>
+    </section>
+    
+</article>

Added: incubator/directory/eve/branches/start/src/docbook/design/triggers-and-procedures.xml
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/docbook/design/triggers-and-procedures.xml	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,121 @@
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V4.1.2.5//EN"
+    "http://www.oasis-open.org/docbook/xml/simple/4.1.2.5/sdocbook.dtd">
+
+
+<article>
+<title>Triggers and Stored Procedures</title>
+
+<para>
+    All of us that have had serious experience using a traditional RDBMS are more
+    than familiar with the use of triggers and stored procedures.  Triggers and
+    stored procedures provide the basic framework for a rich application development
+    platform.  We agree with most architects when we reject the idea of using the
+    integration teir for the development of whole applications.  However it is a
+    place where peices specific to the integration teir can be developed as 
+    components in the overall application.  Sometimes there is no way to avoid 
+    using these component even though they can be ignored for the majority of 
+    applications.  In this way, triggers and stored procedures are a nice feature
+    to have in any data management system if the need requires them.
+</para>
+
+<para>
+    Triggers and stored procedures have played a major role in the database world.
+    Why not enable the facility within the world of the directory?  Directories unlike
+    databases have until recently been a nacent and uncommon technology.  In total 
+    LDAP has existed for less than two decades.  The demand for LDAP is just now 
+    catching on as directories become a staple component in the architect's palette.  
+    The idea of architecting for the constant shared data in a system as well as the 
+    ever changing data is finally manifesting itself.  As applications using directory
+    services increase so do the potential for circumstances requiring the use of 
+    triggers.  Just recently the some effort have begun to incorporate triggers into
+    the LDAP specification as an extention.  The LTAP (Lightweigth Trigger Access 
+    Process) http://ltap.bell-labs.com/ is one step in that direction but it implements
+    LDAP triggers using a gateway process blanketed over an existing directory.  Ldapd
+    incorporates such functionality directly into the server.
+</para>
+
+<para>
+    This chapter explores an experimental trigger and stored procedure subsystem 
+    within the ldapd server.  The LDAP tigger and stored procedure counterparts 
+    are not all that different from those in the world of databases.  The idea is 
+    essentially the same.  A trigger defines the conditions required to fire.  
+    Firing a trigger invokes a stored procedure or an embedded procedure specified 
+    as the body of the trigger.  Hence like a business rule a trigger has a LHS 
+    specifying the conditions for firing it and a RHS specifying the actions to take 
+    when fired.  Our goals are to describe how these subsystems have been designed, 
+    and implemented in ldapd. Consequently we will show how triggers can be specified, 
+    and enabled within a directory information tree served by ldapd.
+</para>
+
+<section>
+    <title>
+        UseCases and Conventions
+    </title>
+    
+    <para>
+        some text here.
+    </para>
+
+    <para>
+        SOME NOTES ON TRIGGERS
+        
+        Triggers are going to need some sort of trigger specification.  This spec
+        must describe whether trigger itself is relicated and whether or not the
+        resultant changes induced by the trigger are replicated.  Conversely we
+        may descide to put these parameters into a stored procedure spec instead
+        that way a timer driven firing of a stored proc may suppress replication.
+        
+        If a trigger itself is replicated then it cannot replicate the effects of
+        the opperation because then the operations would be executed twice on the
+        replicas.  Once by the operation of the replicated trigger, and again by
+        the replicated events fired as the result of the trigger firing on the
+        master. So a trigger is either replicated and does not replicate resultant
+        change driven events or is not replicated and does replicate the resultant
+        events caused by the stored procedures it executes.
+        
+        Sometime there are scenarios when one scheme will be better than the other.
+        For example an involved CPU intensive procedure that updates only one entry
+        like a time triggered summary job may be better off kept on the master without
+        replicating the trigger and only replicating the events triggered by the
+        execution of its stored procedure.  This way the expensive operations is
+        run once and its results are replicated rather than running the operation
+        on every replica.  Conversely if the trigger's execution results in a large
+        number of changes then it may be best to replicate the trigger and not its
+        results because the amount of network communication required may be
+        detrimental.
+    </para>
+        <programlisting>
+            trigger   : CREATE TRIGGER NAME 
+                OWNED BY dn [ ON USERS dn+ ] [ AS dn ] when ENTITY matches DO procedure ;
+            when      : WHEN ( BEFORE | AFTER | FAILED ) 
+                ( ADD | DELETE | MODIFY | READ | BIND | UNBIND )+ ;
+            matches   : MATCHES dn '?' [ scope ] '?' [ filter ] '?' [ FIELDS attribName+ ] ;
+            procedure : NAME | LANG ( JAVA | JYTHON ) '{' code '}' ;
+            
+            
+            attributetype ( UNDEFINED_OID
+                NAME 'op'
+                DESC 'An LDAP Operation'
+                EQUALITY caseIgnoreMatch
+                SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+            objectclass ( UNDEFINED_OID
+                NAME 'trigger'
+                DESC 'LDAP trigger'
+                SUP top
+                MAY ( run-as $  )
+                MUST ( uid $ owner $ op $ when $ match $ procedure ) 
+            )
+
+            LDAPv2 (RFC 1777) and LDAPv3 (RFCs 2251 through 2256) compliant, including support for 
+            extensible schema, referrals, paged results, and change log extensions 
+
+            Dynamically computed virtual attributes for integration with other information sources 
+        </programlisting>
+    <para>
+        
+    
+    </para>
+</section>
+
+</article>

Added: incubator/directory/eve/branches/start/src/images/AddRequestProcessorProcess.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/AdminSchemaContext.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/BackendInterfaces.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/BindRequestProcessorProcess.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/CRUDLifecycle.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/CompareRequestProcessorProcess.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/DelRequestProcessorProcess.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/ExtendedRequestProcessorProcess.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/InputEventHandlerHandleEvent.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/InputModuleRegister.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/ListenerModuleRun.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/ModifyDNRequestProcessorProcess.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/ModifyRequestProcessorProcess.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/ModuleDependencies.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/OutputEventHandlerHandleEvent.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/OutputModuleWrite.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/ReqRespStageFlow.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/RequestEventHandlerHandleEvent.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/ResponseEventHandlerHandleEvent.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/SearchRequestProcessorProcess.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/images/UnBindRequestProcessorProcess.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/AbstractModule.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/AbstractModule.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,123 @@
+/*
+ * $Id: AbstractModule.java,v 1.2 2003/03/13 18:26:29 akarasulu Exp $
+ * $Prologue$
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve ;
+
+import org.apache.avalon.framework.logger.Logger ;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.ContextException;
+
+
+/**
+ * Abstract module class provided for convenience.  Provides start, stop and
+ * logger methods out of the box.  Subclasses that override start and stop must
+ * call start() and stop() super class methods after performing their required
+ * opertations in the respective override.
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.2 $
+ */
+public abstract class AbstractModule
+    implements Module
+{
+    /** The logger used by this LogEnabled module. */
+    private Logger m_logger = null ;
+    /** Member used to track whether or not this module has been started. */
+    private boolean m_hasStarted = false ;
+
+
+
+	public void service(ServiceManager a_manager)
+        throws ServiceException
+    {
+    }
+
+
+	public void initialize()
+        throws Exception
+    {
+    }
+
+
+    /**
+     * Starts this module.  All subclasses much call this super method after
+     * performing their own start tasks.
+     *
+     * @throws Exception of any kind subclasses of which would depend on the
+     * nature of derived concrete modules.
+     */
+    public void start()
+        throws Exception
+    {
+        m_hasStarted = true ;
+    }
+
+
+    /**
+     * Stops this module.  All subclasses much call this super method after
+     * performing their own stop tasks.
+     * 
+     * @throws Exception of any kind subclasses of which would depend on the
+     * nature of derived concrete modules.
+     */
+    public void stop()
+        throws Exception
+    {
+        m_hasStarted = false ;
+    }
+
+
+    /**
+     * Checks to see if this module has started.
+     *
+     * @return true if it has started, false otherwise.
+     */
+    public final boolean hasStarted()
+    {
+        return m_hasStarted ;
+    }
+
+
+    /**
+     * Gets the Logger used by this module to log messages.
+     *
+     * @return this modules Logger.
+     */
+    public final Logger getLogger()
+    {
+        return m_logger ;
+    }
+
+
+    /**
+     * LogEnabled interface implementation which sets this Modules or LogEnabled
+     * class' Logger.
+     *
+     * @param a_logger used by this LogEnabled module.
+     */
+    public void enableLogging(Logger a_logger)
+    {
+        m_logger = a_logger ;
+    }
+
+
+    public void contextualize(Context a_context)
+        throws ContextException
+    {
+        if(getLogger().isDebugEnabled()) {
+        	getLogger().debug(this.getImplementationName() +
+                " executing contextualize phase") ;
+        }
+    }
+}
+

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/Kernel.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/Kernel.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,63 @@
+/*
+ * $Id: Kernel.java,v 1.4 2003/08/22 21:15:54 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve ;
+
+
+import org.apache.avalon.framework.logger.Logger ;
+import org.apache.avalon.framework.service.ServiceManager ;
+
+
+/**
+ * Some very primitive functionality to a kernel used to fire up the server or
+ * just parts of it.
+ */
+public interface Kernel
+{
+    /**
+     * Gets path to the root directory where the sar has been unraveled.
+     */
+    String getRoot() ;
+
+    /**
+     * Sets the root directory where the sar has been unraveled.  Must be set
+     * before bootstraping.
+     */
+    void setRoot( String a_rootDirPath ) ;
+
+    /**
+     * Starts up the kernel starting a single module and all modules it depends
+     * on.  Using this configuration backend subsystems alone can be started
+     * without firing up the entire server.
+     */
+    void bootStrap( String a_module ) throws Exception ;
+
+    /**
+     * Starts up the entire server with all the modules.
+     */
+    void bootStrap() throws Exception ;
+
+    void shutdown() ;
+
+    /**
+     * Gets the service manager used by components to resolve dependent
+     * services exposed by server plugins.
+     */
+    ServiceManager getServiceManager() ;
+
+    /**
+     * Gets the system logger for this Kernel
+     */
+    Logger getLogger() ;
+
+    /**
+     * Gets the component logger for a module in this Kernel.
+     */
+    Logger getLogger( String a_module ) ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/Module.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/Module.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,81 @@
+/*
+ * $Id: Module.java,v 1.4 2003/08/22 21:15:54 akarasulu Exp $
+ * $Prologue$
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve ;
+
+import org.apache.avalon.framework.activity.Startable ;
+import org.apache.avalon.framework.thread.ThreadSafe ;
+import org.apache.avalon.framework.logger.LogEnabled ;
+import org.apache.avalon.framework.logger.Logger ;
+import org.apache.avalon.framework.configuration.Configurable ;
+import org.apache.avalon.framework.service.Serviceable ;
+import org.apache.avalon.framework.activity.Initializable ;
+import org.apache.avalon.framework.context.Contextualizable ;
+
+
+/**
+ * Modules are pluggable black box components that can be started and stopped
+ * and are responsible for their own error handling and logging.  Some types of
+ * modules within ldapd are backend modules and replication modules.
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.4 $
+ */
+public interface Module
+    extends
+    Startable,
+    ThreadSafe,
+    LogEnabled,
+    Serviceable,
+    Configurable,
+    Contextualizable,
+	Initializable
+{
+    /**
+     * Gets the service interface name of this module.
+     *
+     * @return the role of this module's implemented service.
+     */
+    String getImplementationRole() ;
+
+    /**
+     * Gets the name of the implementation.  For example the name of the
+     * Berkeley DB Backend module is "Berkeley DB Backend".
+     *
+     * @return String representing the module implementation type name.
+     */
+	String getImplementationName() ;
+
+    /**
+     * Gets the name of the implementation class.  For example the name of the
+     * Berkeley DB Backend implementation class is <code>
+     * "ldapdd.backend.berkeley.BackendBDb" </code>.
+     *
+     * @return String representing the module implementation's class name.
+     */
+    String getImplementationClassName() ;
+
+
+    /**
+     * Checks to see if this module has already started.
+     *
+     * @return true if the module started, false otherwise.
+     */
+    boolean hasStarted() ;
+
+    /**
+     * Gets the logger used by this module to log messages.
+     *
+     * @return the logger used by this module.
+     */
+    Logger getLogger() ;
+}
+

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/AtomicBackend.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/AtomicBackend.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,40 @@
+/*
+ * $Id: AtomicBackend.java,v 1.5 2003/08/22 21:15:54 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import javax.naming.Name ; 
+import org.apache.eve.schema.Schema ;
+
+
+/**
+ * Atomic backends represent a concrete configurable Directory Information Base
+ *(DIB). They are not composed of other Backends.
+ * 
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.5 $
+ */
+public interface AtomicBackend
+    extends Backend, BackendConfig
+{
+    /**
+     * Role of this service interface as mandated by the avalon framework.
+     */
+    public static final String ROLE = AtomicBackend.class.getName() ;
+
+    /**
+     * Gets the normalized DN of the suffix managed by this AtomicBackend.
+     *
+     * @return Name representing the normalized root suffix for this
+     * AtomicBackend.
+     */
+    Name getSuffix() ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/AttributeAdapter.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/AttributeAdapter.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,198 @@
+/*
+ * $Id: AttributeAdapter.java,v 1.3 2003/04/09 15:51:25 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import java.util.Vector ;
+import java.util.Iterator ;
+import java.util.ArrayList ;
+import java.util.Collection ;
+
+import javax.naming.NamingException ;
+import org.apache.ldap.common.NotImplementedException ;
+import javax.naming.NamingEnumeration ;
+import javax.naming.directory.Attribute ;
+import javax.naming.directory.DirContext ;
+
+
+/**
+ * Attribute adapter which is a simple wrapper around a Collection.
+ */
+public class AttributeAdapter
+    implements Attribute
+{
+	Collection m_collection ;
+	String m_id ;
+
+
+	public AttributeAdapter(Collection a_collection, String a_id) {
+		m_id = a_id ;
+		m_collection = a_collection ;
+	}
+
+
+	public NamingEnumeration getAll() throws NamingException
+	{
+		final Iterator l_list = m_collection.iterator() ;
+		return new NamingEnumeration() {
+			public boolean hasMore() { return l_list.hasNext() ; }
+			public boolean hasMoreElements() { return l_list.hasNext() ; }
+			public Object nextElement() { return l_list.next() ; }
+			public Object next() { return l_list.next() ; }
+			public void close() {}
+		} ;
+	}
+
+
+	public Object get() throws NamingException
+	{
+		if(m_collection instanceof ArrayList) {
+			ArrayList l_al = (ArrayList) m_collection ;
+			if(l_al.size() > 0) {
+				return l_al.get(0) ;
+			} else {
+				return null ;
+			}
+		}
+
+		throw new NotImplementedException() ;
+	}
+
+
+	public int size()
+	{
+		return m_collection.size() ;
+	}
+
+
+	public String getID()
+	{
+		return m_id ;
+	}
+
+
+	public boolean contains(Object attrVal)
+	{
+		return m_collection.contains(attrVal) ;
+	}
+
+
+	public boolean add(Object attrVal)
+	{
+		if(m_collection.contains(attrVal)) {
+			return false ;
+		} else {
+			m_collection.add(attrVal) ;
+			return true ;
+		}
+	}
+
+
+	public boolean remove(Object attrval)
+	{
+		return m_collection.remove(attrval) ;
+	}
+
+
+	public void clear()
+	{
+		m_collection.clear() ;
+	}
+
+
+	public DirContext getAttributeSyntaxDefinition() throws NamingException
+	{
+		throw new UnsupportedOperationException(
+			"AttributeAdapter does not support schema info right now.") ;
+	}
+
+
+	public DirContext getAttributeDefinition() throws NamingException
+	{
+		throw new UnsupportedOperationException(
+			"AttributeAdapter does not support schema info right now.") ;
+	}
+
+
+	public Object clone() {
+		throw new UnsupportedOperationException(
+			"AttributeAdapter for event delivery does not support clone().") ;
+	}
+
+	//----------- Methods to support ordered multivalued attributes
+
+	public boolean isOrdered()
+	{
+		if(m_collection instanceof Vector ||
+		   m_collection instanceof ArrayList) {
+			return true ;
+		}
+
+		return false ;
+	}
+
+
+	public Object get(int ix) throws NamingException
+	{
+		if(m_collection instanceof ArrayList) {
+			return ((ArrayList) m_collection).get(ix) ;
+		} else if(m_collection instanceof Vector) {
+			return ((Vector) m_collection).get(ix) ;
+		}
+
+		throw new UnsupportedOperationException(
+			"Collection of " + m_collection.getClass().getName()
+			+ "does not support ordered access to attribute values") ;
+	}
+
+
+	public Object remove(int ix)
+	{
+		if(m_collection instanceof ArrayList) {
+			return ((ArrayList) m_collection).remove(ix) ;
+		} else if(m_collection instanceof Vector) {
+			return ((Vector) m_collection).remove(ix) ;
+		}
+
+		throw new UnsupportedOperationException(
+			"Collection of " + m_collection.getClass().getName()
+			+ "does not support ordered removal of attribute values") ;
+	}
+
+
+	public void add(int ix, Object attrVal)
+	{
+		if(m_collection instanceof ArrayList) {
+			((ArrayList) m_collection).add(ix, attrVal) ;
+			return ;
+		} else if(m_collection instanceof Vector) {
+			((Vector) m_collection).add(ix, attrVal) ;
+			return ;
+		}
+
+		throw new UnsupportedOperationException(
+			"Collection of " + m_collection.getClass().getName()
+			+ "does not support ordered addition to attribute values") ;
+	}
+
+
+	public Object set(int ix, Object attrVal)
+	{
+		if(m_collection instanceof ArrayList) {
+			return ((ArrayList) m_collection).set(ix, attrVal) ;
+		} else if(m_collection instanceof Vector) {
+			return ((Vector) m_collection).set(ix, attrVal) ;
+		}
+
+		throw new UnsupportedOperationException(
+			"Collection of " + m_collection.getClass().getName()
+			+ "does not support ordered alteration of attribute values") ;
+	}
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/AttributesAdapter.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/AttributesAdapter.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,236 @@
+/*
+ * $Id: AttributesAdapter.java,v 1.4 2003/04/09 15:51:25 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import java.util.Vector ;
+import java.util.Iterator ;
+import java.util.ArrayList ;
+import java.util.Collection ;
+
+
+import javax.naming.NamingException ;
+import javax.naming.NamingEnumeration ;
+import javax.naming.directory.Attribute ;
+import javax.naming.directory.DirContext ;
+import javax.naming.directory.Attributes ;
+
+import org.apache.ldap.common.NotImplementedException ;
+
+import org.apache.avalon.framework.CascadingRuntimeException ;
+
+
+/**
+ * An Attributes interface adapter around an LdapEntry.
+ */
+public class AttributesAdapter
+    implements Attributes
+{
+	private final LdapEntry m_entry ;
+
+
+    public AttributesAdapter(LdapEntry a_entry) {
+        m_entry = a_entry ;
+    }
+
+
+    /**
+      * Determines whether the attribute set ignores the case of
+      * attribute identifiers when retrieving or adding attributes.
+      * @return true if case is ignored; false otherwise.
+      */
+    public boolean isCaseIgnored()
+    {
+        return true ;
+    }
+
+
+    /**
+      * Retrieves the number of attributes in the attribute set.
+      *
+      * @return The nonnegative number of attributes in this attribute set.
+      */
+    public int size()
+    {
+        return m_entry.attributes().size() ;
+    }
+
+    /**
+      * Retrieves the attribute with the given attribute id from the
+      * attribute set.
+      *
+      * @param attrID The non-null id of the attribute to retrieve.
+      * 	  If this attribute set ignores the character
+      *		  case of its attribute ids, the case of attrID
+      *		  is ignored.
+      * @return The attribute identified by attrID; null if not found.
+      * @see #put
+      * @see #remove
+      */
+    public Attribute get(String attrId)
+    {
+        if(m_entry.hasAttribute(attrId)) {
+	        return new AttributeAdapter(m_entry.getMultiValue(attrId), attrId) ;
+        }
+
+        return null ;
+    }
+
+
+    /**
+      * Retrieves an enumeration of the attributes in the attribute set.
+      * The effects of updates to this attribute set on this enumeration
+      * are undefined.
+      *
+      * @return A non-null enumeration of the attributes in this attribute set.
+      *		Each element of the enumeration is of class <tt>Attribute</tt>.
+      * 	If attribute set has zero attributes, an empty enumeration 
+      * 	is returned.
+      */
+    public NamingEnumeration getAll()
+    {
+        final Iterator l_list = m_entry.attributes().iterator() ;
+		return new NamingEnumeration() {
+			public boolean hasMore() { return l_list.hasNext() ; }
+			public boolean hasMoreElements() { return l_list.hasNext() ; }
+			public Object nextElement()
+            {
+                String l_attrId = (String) l_list.next() ;
+                Attribute l_attr = new AttributeAdapter(
+                    m_entry.getMultiValue(l_attrId), l_attrId) ;
+                return l_attr ;
+            }
+			public Object next()
+            {
+                String l_attrId = (String) l_list.next() ;
+                Attribute l_attr = new AttributeAdapter(
+                    m_entry.getMultiValue(l_attrId), l_attrId) ;
+                return l_attr ;
+            }
+			public void close() {}
+		} ;
+    }
+
+
+    /**
+      * Retrieves an enumeration of the ids of the attributes in the
+      * attribute set.
+      * The effects of updates to this attribute set on this enumeration
+      * are undefined.
+      *
+      * @return A non-null enumeration of the attributes' ids in
+      * 	this attribute set. Each element of the enumeration is
+      *		of class String.
+      * 	If attribute set has zero attributes, an empty enumeration 
+      * 	is returned.
+      */
+    public NamingEnumeration getIDs()
+    {
+        final Iterator l_list = m_entry.attributes().iterator() ;
+		return new NamingEnumeration() {
+			public boolean hasMore() { return l_list.hasNext() ; }
+			public boolean hasMoreElements() { return l_list.hasNext() ; }
+			public Object nextElement() { return l_list.next() ; }
+			public Object next() { return l_list.next() ; }
+			public void close() {}
+		} ;
+    }
+
+
+    /**
+      * Adds a new attribute to the attribute set.
+      *
+      * @param attrID 	non-null The id of the attribute to add.
+      * 	  If the attribute set ignores the character
+      *		  case of its attribute ids, the case of attrID
+      *		  is ignored.
+      * @param val	The possibly null value of the attribute to add.
+      *			If null, the attribute does not have any values.
+      * @return The Attribute with attrID that was previous in this attribute set;
+      * 	null if no such attribute existed.
+      * @see #remove
+      */
+    public Attribute put(String attrID, Object val)
+    {
+        try {
+        	m_entry.addValue(attrID, val) ;
+        } catch(NamingException e) {
+            throw new CascadingRuntimeException("Naming exception thrown", e) ;
+        }
+
+        return new AttributeAdapter(m_entry.getMultiValue(attrID), attrID) ;
+    }
+
+
+    /**
+      * Adds a new attribute to the attribute set.
+      *
+      * @param attr 	The non-null attribute to add.
+      * 		If the attribute set ignores the character
+      *		  	case of its attribute ids, the case of
+      * 		attr's identifier is ignored.
+      * @return The Attribute with the same ID as attr that was previous 
+      * 	in this attribute set;
+      * 	null if no such attribute existed.
+      * @see #remove
+      */
+    public Attribute put(Attribute attr)
+    {
+        try {
+			NamingEnumeration l_list = attr.getAll() ;
+			while(l_list.hasMore()) {
+				m_entry.addValue(attr.getID(), l_list.next()) ;
+			}
+	
+			return new AttributeAdapter(m_entry.getMultiValue(attr.getID()),
+				attr.getID());
+        } catch(NamingException e) {
+            throw new CascadingRuntimeException("Naming exception thrown", e) ;
+        }
+    }
+
+
+    /**
+      * Removes the attribute with the attribute id 'attrID' from
+      * the attribute set. If the attribute does not exist, ignore.
+      *
+      * @param attrID 	The non-null id of the attribute to remove.
+      * 		If the attribute set ignores the character
+      *		  	case of its attribute ids, the case of 
+      *               	attrID is ignored.
+      * @return The Attribute with the same ID as attrID that was previous 
+      * 	in the attribute set;
+      * 	null if no such attribute existed.
+      */
+    public Attribute remove(String attrId)
+    {
+        try {
+			Attribute l_attr =
+				new AttributeAdapter(m_entry.getMultiValue(attrId), attrId) ;
+			m_entry.removeValues(attrId) ;
+			return l_attr ;
+        } catch(NamingException e) {
+            throw new CascadingRuntimeException("Naming exception thrown", e) ;
+        }
+    }
+
+
+    /**
+      * Makes a copy of the attribute set.
+      * The new set contains the same attributes as the original set:
+      * the attributes are not themselves cloned.
+      *
+      * @return A non-null copy of this attribute set.
+      */
+    public Object clone() {
+        throw new UnsupportedOperationException(
+            "AttributesAdapter for event delivery does not allow clone ops.") ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/Backend.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/Backend.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,375 @@
+/*
+ * $Id: Backend.java,v 1.9 2003/03/27 16:30:37 jmachols Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import javax.naming.Name ;
+import javax.naming.NamingException ;
+import javax.naming.directory.DirContext ;
+import javax.naming.directory.SearchControls ;
+
+import org.apache.ldap.common.filter.ExprNode ;
+import org.apache.eve.client.ClientManagerSlave ;
+
+
+/**
+ * Backend modules store entries in a manner specific to a backing store.
+ * 
+ * Backends may be memory resident non persistant stores, use flat files, xml,
+ * relational databases, non relational databases, or even other surrogate LDAP
+ * directories to store entries in whatever scheme they see fit. No limitations
+ * are placed on how Backends store or manage entries.
+ *
+ * These interfaces simply define a means to get to Entry attributes in a
+ * standard way.  Hence this interface establishes one of the primary contracts
+ * that the ldap server holds with a pluggable backend module.  Other
+ * complementary interfaces in this package are artifacts that are required to
+ * interface with a Backend or the attribute contents of entries.  Backend
+ * implementations must uphold these server-backend contracts.
+ *
+ * Related Documentation:
+ * <ul>
+ * <li><a href=backendhowto.html>HowTo Build A Backend</a></li>
+ * <li><a href=dnnormalization.html>Distinguished Name Normalization</a></li>
+ * <li><a href=contract.html>Server-Backend Contract.</a></li>
+ * <li><a href=http://afs.wu-wien.ac.at/manuals/rfc-ldap.html>
+ * Relevant LDAP RFCs</a></li></ul>
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: jmachols $
+ * @version $Revision: 1.9 $
+ */
+public interface Backend extends ClientManagerSlave
+{
+    /**
+     * Constant used to represent object level search scope which equals
+     * SearchControls.OBJECT_SCOPE.
+     */
+    int BASE_SCOPE = SearchControls.OBJECT_SCOPE ;
+    /**
+     * Constant used to represent single level search scope which equals
+     * SearchControls.ONELEVEL_SCOPE.
+     */
+    int SINGLE_SCOPE = SearchControls.ONELEVEL_SCOPE ;
+    /**
+     * Constant used to represent subtree level search scope which equals
+     * SearchControls.SUBTREE_SCOPE.
+     */
+    int SUBTREE_SCOPE = SearchControls.SUBTREE_SCOPE ;
+
+    /** Constant used to represent attribute/value addition. */
+    int ADD_ATTRIBUTE = DirContext.ADD_ATTRIBUTE ;
+    /** Constant used to represent attribute/value removal. */
+    int REMOVE_ATTRIBUTE = DirContext.REMOVE_ATTRIBUTE ;
+    /** Constant used to represent attribute/value replacement. */
+    int REPLACE_ATTRIBUTE = DirContext.REPLACE_ATTRIBUTE ;
+
+
+    ///////////////////////////
+    // Entry CRUD Operations //
+    ///////////////////////////
+
+
+    /**
+     * Creates a new Entry without adding it to the database.  The entry is
+     * invalid until it is added to the instantiating backend via a create()
+     * call. The new Entry contains a single operational attribute: the DN of
+     * the Entry in the case and format specified by the user.  Hence calls to
+     * the <code>getEntryDN()</code> method of the Entry shall return the DN
+     * argument to this method.  Calls to the <code>getNormalizedDN()</code>
+     * method will return the normalized DN.
+     * 
+     * To comply with the server backend contract the DN argument must not be
+     * tampered with by a normalizer.  This method expects a_dn to be the
+     * original user provided unformatted DN.
+     *
+     * @param a_dn the user provided distinguished name of the new Entry.
+     * @return the newly instantiated Entry to be added to this Backend via the
+     * create() method.
+     * @throws javax.naming.NameNotFoundException when a component of the parent cannot be
+     * resolved because it is not bound - the parent entry of the new Entry
+     * must be bound for this factory method.
+     * @throws javax.naming.InvalidNameException when a_dn is not syntactically correct.
+     * @throws javax.naming.NameAlreadyBoundException when an Entry with a_dn already
+     * exists.
+     * @throws BackendException when a backing store error occurs.
+     */
+    LdapEntry newEntry(String a_dn)
+        throws BackendException, NamingException ;
+
+    /**
+     * Deletes (cru<b>D</b>) an entry from the Backend and all cached
+     * referrences to it.  The Entry cache if any is purged of referrences to
+     * the Entry.
+     *
+     * @param an_entry Entry to be deleted
+     * @throws BackendException when removal from the backend fails due to a
+     * backing store error, or an_entry is <b>NOT</b> valid, or is not managed
+     * by this Backend.
+     * @throws javax.naming.ContextNotEmptyException when non leaf entries are attempted to
+     * be deleted.
+     */
+    void delete(LdapEntry an_entry)
+        throws BackendException, NamingException ;
+
+    /**
+     * Creates (<b>C</b>rud) an Entry in this Backend.
+     *
+     * @param an_entry to put into this Backend.
+     * @throws BackendException if anything goes drastically wrong with this
+     * Backend while attempting to perform the operation, or if an Entry with
+     * the same DN already exists within this Backend, or if the Entry is not
+     * managed by this Backend.
+     * @throws javax.naming.directory.SchemaViolationException if the attributes supplied with an
+     * entry do not satisfy objectclass schema constraints.
+     * @throws javax.naming.NameAlreadyBoundException if an Entry with a_dn already exists,
+     * this would occur if another thread creates an Entry with its DN between
+     * the call to newEntry and this method.
+     * @throws javax.naming.directory.InvalidAttributesException when all the mandatory attributes
+     * required by the objectclasses of the object are not present.
+     */
+    void create(LdapEntry an_entry)
+        throws BackendException, NamingException ;
+
+
+    /**
+     * Read (c<b>R</b>ud) an Entry from this Backend based on it's unique DN.
+     *
+     * The distinguished name argument is NOT presumed to be normalized in
+     * accordance with schema attribute syntax and attribute matching rules.
+     * The DN is also NOT presumed to be syntacticly correct or within the
+     * namespace of this directory information base.  Unaware of normalization
+     * this method will attempt to normalize any DN arguements.  Apriori
+     * normalization would be redundant.
+     *
+     * @param a_dn the unique distinguished name of the entry to get.
+     * @return the entry identified by a_dn or null if one by that name does 
+     * not exist.
+     * @throws BackendException if anything goes drastically wrong with this
+     * Backend while attempting to perform the operation.
+     * @throws javax.naming.NameNotFoundException when a component of the name cannot be
+     * resolved because it is not bound.
+     * @throws javax.naming.InvalidNameException if a_dn is not syntactically correct.
+     */
+    LdapEntry read(Name a_dn)
+        throws BackendException, NamingException ;
+
+    /**
+     * Updates (cr<b>U</b>d) the valid Entry by presuming that it has been
+     * altered.  The alterations may effect multiple attributes by adding
+     * values, removing values or changing values.  The Entry must be valid
+     * for this operation to take place.
+     *
+     * @param an_entry Entry to modify
+     * @throws BackendException when the modification fails, or an_entry is not
+     * valid.
+     * @throws javax.naming.directory.SchemaViolationException if the attributes supplied with an
+     * entry do not satisfy objectclass schema constraints.
+     * @throws javax.naming.directory.InvalidAttributesException when all the mandatory attributes
+     * required by the objectclasses of the object are not present.
+     */
+    void update(LdapEntry an_entry)
+        throws BackendException, NamingException ;
+
+
+    //////////////////////////////////////
+    // Parent Child Relation Operations //
+    //////////////////////////////////////
+
+
+    /**
+     * Lists the children of an entry identified by a_dn.  The returned Cursor
+     * enumerates through the children.<br>
+     * <br>
+     * The distinguished name argument is NOT presumed to be normalized in
+     * accordance with schema attribute syntax and attribute matching rules.
+     * The DN is also NOT presumed to be syntacticly correct or within the
+     * namespace of this directory information base.  Unaware of normalization
+     * this method will attempt to normalize any DN arguements.  Apriori
+     * normalization would be redundant.
+     *
+     * @param a_parentDN the parent entry's distinguished name.
+     * @return a cursor enumerating over the child entries of the parent.
+     * @throws BackendException if anything goes drastically wrong with this
+     * Backend while attempting to perform the operation.
+     * @throws javax.naming.NameNotFoundException when a component of the name cannot be
+     * resolved because it is not bound.
+     * @throws javax.naming.InvalidNameException if a_parentDN is not syntactically correct.
+     */
+    Cursor listChildren(Name a_parentDN)
+        throws BackendException, NamingException ;
+
+    /**
+     * Gets the parent of a child entry.
+     * 
+     * The distinguished name argument is NOT presumed to be normalized in
+     * accordance with schema attribute syntax and attribute matching rules.
+     * The DN is also NOT presumed to be syntacticly correct or within the
+     * namespace of this directory information base.  Unaware of normalization
+     * this method will attempt to normalize any DN arguements.  Apriori
+     * normalization would be redundant.
+     *
+     * @param a_childDN the distinguished name of the child entry.
+     * @return the parent entry of an entry identified by a child DN or the
+     * child entry if the child is the suffix.
+     * @throws BackendException if anything goes drastically wrong with this
+     * Backend while attempting to perform the operation.
+     * @throws javax.naming.NameNotFoundException when a component of the name cannot be
+     * resolved because it is not bound.
+     * @throws javax.naming.InvalidNameException if a_childDN is not syntactically correct.
+     */
+    LdapEntry getParent(Name a_childDN)
+        throws BackendException, NamingException ;
+
+    /**
+     * Checks to see if an entry with a distinguished name exists within this
+     * Backend.
+     * 
+     * The distinguished name argument is NOT presumed to be normalized in
+     * accordance with schema attribute syntax and attribute matching rules.
+     * The DN is also NOT presumed to be syntacticly correct or within the
+     * namespace of this directory information base.  Unaware of normalization
+     * this method will attempt to normalize any DN arguements.  Apriori
+     * normalization would be redundant.
+     *
+     * @param a_dn the distinguished name of the entry to check for.
+     * @return true if the entry exists within this Backend instance.
+     * @throws BackendException if anything goes drastically wrong with this
+     * Backend while attempting to perform the operation.
+     * @throws javax.naming.InvalidNameException if a_dn is not syntactically correct.
+     */
+    boolean hasEntry(Name a_dn)
+        throws BackendException, NamingException ;
+
+
+    /**
+     * Tests to see if this entry is a/the suffix Entry of this Backend.
+     * Unified composite backends will return true if the Entry is a valid
+     * suffix within the set of Backends composing it.
+     *
+     * @param an_entry the Entry to test if it is the suffix of this Backend or one of
+     * its composing Backends if this Backend is a UnifiedBackend.
+     * @return true if the Entry's DN is equivalent to the DN of this Backend,
+     * or one contained by a UnifiedBackend, otherwise false.
+     */
+    boolean isSuffix(LdapEntry an_entry)
+        throws NamingException ;
+
+    /**
+     * Checks to see if a user dn is the dn of this backend or one of its
+     * AtomicBackends if it is a UnifiedBackend.
+     *
+     * @param a_userDn the user dn to check for.
+     * @return true if a_userDn represents an admin dn, false otherwise.
+     * @throws NamingException if their is a problem with the specified user dn.
+     */
+    boolean isAdminUser(Name a_userDn)
+        throws NamingException ;
+
+    ////////////////////////
+    // Special Operations //
+    ////////////////////////
+
+    /**
+     * Searches for candidate entries on the backend starting on a base DN 
+     * using a search filter with search controls.
+     * 
+     * The distinguished name argument is NOT presumed to be normalized in
+     * accordance with schema attribute syntax and attribute matching rules.
+     * The DN is also NOT presumed to be syntacticly correct or within the
+     * namespace of this directory information base.  Unaware of normalization
+     * this method will attempt to normalize any DN arguements.  Apriori
+     * normalization would be redundant.
+     *
+     * W O R K   I N   P R O G R E S S
+     *
+     * @param a_filter String representation of an LDAP search filter.
+     * @param a_baseDN String representing the base of the search.
+     * @param a_scope SearchControls governing how this search is to be
+     * conducted.
+     * @throws BackendException on Backend errors or when the operation cannot
+     * proceed due to a malformed search filter, a non-existant search base, or
+     * inconsistant search controls.
+     * @throws javax.naming.InvalidNameException if a_baseDN is not syntactically correct.
+     * @throws javax.naming.NameNotFoundException when a component of a_baseDN cannot be
+     * resolved because it is not bound.
+     * @throws javax.naming.directory.InvalidSearchFilterException when the specification of a search
+     * filter is invalid. The expression of the filter may be invalid, or there
+     * may be a problem with one of the parameters passed to the filter.
+     * @throws javax.naming.directory.InvalidSearchControlsException when the specification of the
+     * SearchControls for a search operation is invalid. For example, if the
+     * scope is set to a value other than OBJECT_SCOPE, ONELEVEL_SCOPE,
+     * SUBTREE_SCOPE, this exception is thrown.
+     */
+    Cursor search(ExprNode a_filter, Name a_baseDN, int a_scope)
+        throws BackendException, NamingException ;
+
+    /**
+     * Modifies the relative distinguished name (RDN) of an entry
+	 * without changing any parent child relationships.  This call
+	 * has the side effect of altering the distinguished name of
+	 * descendent entries if they exist.  The boolean argument will
+	 * optionally remove the existing RDN attribute value pair
+	 * replacing it with the new RDN attribute value pair.  If other
+	 * RDN attribute value pairs exist besides the current RDN they
+	 * will be spared.
+     *
+     * @param an_entry the entry whose RDN is to be modified.
+     * @param a_newRdn the new Rdn that is to replace the current Rdn.
+     * @param a_deleteOldRdn deletes the old Rdn attribute value pair if true.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    void modifyRdn(LdapEntry an_entry, Name a_newRdn, boolean a_deleteOldRdn)
+	    throws BackendException, NamingException ;
+
+    /**
+     * Moves a child entry without changing the RDN under a new parent
+     * entry.  This effects the parent child relationship between the
+     * parent entry and the child entry.  The index for the child
+     * mapping it to the current parent is destroyed and a new index
+     * mapping it to the new parent is created.  As a side effect the
+     * name of the child entry and all its descendants will reflect the
+     * move within the DIT to a new parent.  The old parent prefix to
+     * the distinguished names of the child and its descendents will be
+     * replaced by the new parent DN prefix.
+     *
+     * @param a_parentEntry the parent the child is to subordinate to.
+     * @param a_childEntry the child to be moved under the parent.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    void move(LdapEntry a_parentEntry, LdapEntry a_childEntry)
+	    throws BackendException, NamingException ;
+
+    /**
+     * This overload combines the first two method operations into one.
+     * It changes the Rdn and the parent prefix at the same time while
+	 * recursing name changes to all descendants of the child entry. It
+	 * is obviously more complex than the other two operations alone and
+	 * involves changes to both parent child indices and DN indices.
+     *
+     * @param a_parentEntry the parent the child is to subordinate to.
+     * @param a_childEntry the child to be moved under the parent.
+     * @param a_newRdn the new Rdn that is to replace the current Rdn.
+     * @param a_deleteOldRdn deletes the old Rdn attribute value pair if true.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    void move(LdapEntry a_parentEntry, LdapEntry a_childEntry,
+	    Name a_newRdn, boolean a_deleteOldRdn)
+	    throws BackendException, NamingException ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/BackendConfig.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/BackendConfig.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,119 @@
+/*
+ * $Id: BackendConfig.java,v 1.4 2003/03/13 18:26:41 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+import java.util.Iterator ;
+import javax.naming.InvalidNameException ;
+
+import org.apache.avalon.framework.configuration.Configuration ;
+import org.apache.avalon.framework.configuration.ConfigurationException ;
+import javax.naming.Name;
+
+
+/**
+ * Interface used to manage the configuration of an AtomicBackend.
+ */
+public interface BackendConfig
+{
+    /** The default entry cache size. */
+    int DEFAULT_ENTRY_CACHESZ = 1000 ;
+
+    /**
+     * Tests to see if the backend of this configuration is operating in read
+     * only mode.  If true alterations to the backend will result in
+     * ReadOnlyExceptions being thrown at runtime.
+     *
+     * @return true if the backend is in read only mode false otherwise.
+     */
+    boolean isReadOnly() ;
+
+    /**
+     * Sets the read only operational mode of a backend using this
+     * configuration.
+     *
+     * @param a_isReadOnlyMode true if backend is to be configured for read 
+     * only mode false otherwise.
+     */
+    void setReadOnly(boolean a_isReadOnlyMode) ;
+
+    /**
+     * Sets the maximum size the entry cache may grow to.
+     *
+     * @param a_numMaxEntries the maximum number of entries that can be held.
+     * @throws BackendException if the configurable module has started and is 
+     * not Reconfigurable.
+     */
+    void setEntryCacheSize(int a_numMaxEntries)
+        throws BackendException ;
+
+    /**
+     * Gets the absolute path to the working directory for the configurable 
+     * backend.
+     *
+     * @return the absolute working directory path.
+     */
+    String getWorkingDirPath() ;
+
+    /**
+     * Sets the working directory path for implementation specific files or 
+     * logs if any.
+     *
+     * @param a_dirPath the absolute path to the working directory for the 
+     * configurable backend.
+     * @throws BackendException if the configurable module has started and is 
+     * not Reconfigurable.
+     */
+    void setWorkingDirPath(String a_dirPath)
+        throws BackendException ;
+
+    /**
+     * Sets the password for the admin user for the configurable backend.
+     *
+     * @param an_adminDN the distinguished name of the backend admin.
+     * @throws InvalidNameException if an_adminDN does not conform to DN 
+     * syntax.
+     * @throws BackendException if the configurable module has started and is 
+     * not Reconfigurable.
+     * @throws InvalidNameException if an_adminDN not in correct DN syntax 
+     */
+    void setAdminUserDN(Name an_adminDN)
+        throws BackendException, InvalidNameException ;
+
+    /**
+     * Gets the admin user distinguished name for the configurable backend.
+     *
+     * @return the distinguished name of the admin user.
+     */
+    Name getAdminUserDN() ;
+
+    /**
+     * Sets the password for the admin user for the configurable backend.
+     *
+     * @param an_adminPassword the distinguished name of the backend admin.
+     * @throws BackendException if the configurable module has started and is 
+     * not Reconfigurable.
+     */
+    void setAdminUserPassword(String an_adminPassword)
+        throws BackendException ;
+
+    /**
+     * Gets the encrypted password of the user in base 64 encoded format.
+     *
+     * @return base64 encoded cypher.
+     */
+    String getAdminUserPassword() ;
+
+    /**
+     * Gets the maximum size the entry cache may grow to.
+     *
+     * @return the maximum number of entries that can be held.
+     */
+    int getEntryCacheSize() ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/BackendException.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/BackendException.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,44 @@
+/*
+ * $Id: BackendException.java,v 1.2 2003/03/13 18:26:42 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+import org.apache.avalon.framework.CascadingException ;
+
+
+/**
+ * This exception is thrown when Error encountered on backend operation.
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.2 $
+ */
+public class BackendException
+    extends CascadingException
+{
+    /**
+     * Constructs an Exception with a detailed message.
+     * @param a_message The message associated with the exception.
+     */
+    public BackendException(String a_message)
+    {
+        super(a_message) ;
+    }
+
+
+    /**
+     * Constructs an Exception with a detailed message and an error.
+     * @param a_message The message associated with the exception.
+     */
+    public BackendException(String a_message, Throwable a_throwable)
+    {
+        super(a_message, a_throwable) ;
+    }
+}
+

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/BackendModule.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/BackendModule.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,482 @@
+/*
+ * $Id: BackendModule.java,v 1.10 2003/08/22 21:15:54 akarasulu Exp $
+ * $Prologue$
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import org.apache.eve.schema.Schema ;
+import org.apache.eve.AbstractModule ;
+import org.apache.eve.client.ClientManager ;
+import org.apache.eve.schema.SchemaManager ;
+
+import java.io.File ;
+import java.util.Iterator ;
+import java.util.Collection ;
+import java.util.NoSuchElementException ;
+
+import javax.naming.Name ;
+import javax.naming.NamingException ;
+import javax.naming.InvalidNameException ;
+import javax.naming.NameNotFoundException ;
+
+import org.apache.commons.collections.LRUMap ;
+import org.apache.commons.collections.MultiMap ;
+import org.apache.commons.collections.MultiHashMap ;
+
+import org.apache.avalon.phoenix.BlockContext ;
+
+import org.apache.avalon.framework.context.Context ;
+import org.apache.avalon.framework.service.ServiceManager ;
+import org.apache.avalon.framework.service.ServiceException ;
+import org.apache.avalon.framework.context.ContextException ;
+import org.apache.avalon.framework.configuration.Configuration ;
+import org.apache.avalon.framework.configuration.ConfigurationException ;
+
+
+public abstract class BackendModule
+    extends AbstractModule
+    implements AtomicBackend
+{
+    //
+    // Configuration Variables
+	//
+
+    protected boolean m_isReadOnly = false ;
+    protected Name m_suffix = null ;
+	protected String m_wkdirPath = null ;
+    protected Name m_adminUser = null ;
+    protected String m_adminPassword = null ;
+    protected BlockContext m_context ;
+
+    protected UnifiedBackend m_nexus = null ;
+    protected ClientManager m_clientMan = null ;
+    protected SchemaManager m_schemaManager = null ;
+	protected Schema m_schema = null ;
+    protected LRUMap m_cache = new LRUMap(1000) ;
+	protected MultiMap m_suffixEntry = new MultiHashMap() ;
+
+
+    /**
+     * Gets the normalized suffix name of this BackendModule.  The name is the
+     * normalized name found in the configuration for the backend within the
+     * suffix tag.
+     *
+     * @return the normalized suffix name for this BackendModule
+     */
+    public Name getSuffix()
+    {
+        return m_suffix ;
+    }
+
+
+    public Schema getSchema()
+    {
+        return m_schema ;
+    }
+
+
+    public boolean isReadOnly()
+    {
+        return this.m_isReadOnly ;
+    }
+
+
+    public void setReadOnly(boolean a_isReadOnlyMode)
+    {
+        this.m_isReadOnly = a_isReadOnlyMode ;
+    }
+
+
+    public void setEntryCacheSize(int a_numMaxEntries)
+        throws BackendException
+    {
+        this.m_cache.setMaximumSize(a_numMaxEntries) ;
+    }
+
+
+    public String getWorkingDirPath()
+    {
+        return this.m_wkdirPath ;
+    }
+
+
+    /**
+     * Sets the working directory path for implementation specific files or logs
+     * if any.
+     *
+     * @param a_dirPath the absolute path to the working directory for the
+     * configurable backend
+     * @throws BackendException if the configurable module has started and is
+     * not Reconfigurable.
+     */
+    public void setWorkingDirPath(String a_dirPath)
+        throws BackendException
+    {
+        if (hasStarted()) {
+            throw new BackendException("Cannot change working directory path "
+                + "after backend has started!") ;
+        }
+
+        File l_dir = new File(a_dirPath) ;
+
+        if (!l_dir.exists()) {
+            throw new BackendException(a_dirPath + " does not exist") ;
+        } else if (!l_dir.canWrite()) {
+            throw new BackendException("Cannot read " + a_dirPath) ;
+        } else if (!l_dir.canWrite()) {
+            throw new BackendException("Cannot write to " + a_dirPath) ;
+        } else if (!l_dir.isDirectory()) {
+            throw new BackendException(a_dirPath + " is not a directory.") ;
+        }
+
+        this.m_wkdirPath = l_dir.getAbsolutePath() ;
+    }
+
+
+    public void setAdminUserDN(Name an_adminDN)
+        throws BackendException
+    {
+        this.m_adminUser = an_adminDN ;
+    }
+
+
+    public Name getAdminUserDN()
+    {
+        return m_adminUser ;
+    }
+
+
+    public void setAdminUserPassword(String an_adminPassword)
+        throws BackendException
+    {
+        this.m_adminPassword = an_adminPassword ;
+    }
+
+
+    public String getAdminUserPassword()
+    {
+        return this.m_adminPassword ;
+    }
+
+
+    public int getEntryCacheSize()
+    {
+        return this.m_cache.getMaximumSize() ;
+    }
+
+
+    public void initialize()
+        throws Exception
+    {
+        if(hasEntry(getSuffix())) {
+            getLogger().info("Suffix entry exists skipping initial creation!") ;
+            return ;
+        }
+
+		getLogger().info("Suffix entry DOES NOT EXIST! " +
+            "Creating Suffix entry for the first time.") ;
+
+        // Extract User Provided Distinguished Name and create the new suffix
+        // root entry.
+        Collection l_col = (Collection) m_suffixEntry.get(Schema.DN_ATTR) ;
+        if(l_col == null) {
+            l_col = (Collection) m_suffixEntry.get("dn") ;
+        }
+        if(l_col == null) {
+			throw new BackendException("Cannot find a 'distinguishedName' or "
+                + "'dn' attribute in the suffix entry!") ;
+        }
+        String l_updn = (String) l_col.iterator().next() ;
+
+        LdapEntry l_entry = newEntry(l_updn) ;
+        Iterator l_keys = m_suffixEntry.keySet().iterator() ;
+        while(l_keys.hasNext()) {
+			String l_key = (String) l_keys.next() ;
+            if(l_key.toLowerCase().trim().equals(Schema.DN_ATTR) ||
+               l_key.toLowerCase().trim().equals("dn"))
+            {
+                continue ;
+            }
+
+            Iterator l_values = ((Collection)
+                m_suffixEntry.get(l_key)).iterator() ;
+            while(l_values.hasNext()) {
+                l_entry.addValue(l_key, l_values.next()) ;
+            }
+        }
+
+        create(l_entry) ;
+    }
+
+
+    /**
+     * All derived modules should call super.service first.
+     */
+    public void service(ServiceManager a_manager)
+        throws ServiceException
+    {
+        m_schemaManager
+            = (SchemaManager) a_manager.lookup(SchemaManager.ROLE) ;
+        m_nexus = (UnifiedBackend) a_manager.lookup(UnifiedBackend.ROLE) ;
+    }
+
+
+    public void start()
+        throws Exception 
+    {
+        super.start() ;
+        m_nexus.register( this ) ;
+    }
+
+
+    public void stop()
+        throws Exception 
+    {
+        m_nexus.unregister( this ) ;
+        super.stop() ;
+    }
+
+
+    public void registerClientManager( ClientManager a_manager )
+    {
+        m_clientMan = a_manager ;
+    }
+
+
+    ////////////////////////////////////////////
+    // Configuration Interface Implementation //
+    ////////////////////////////////////////////
+
+
+    /** DN of backend suffix property tag. */
+	public static final String SUFFIX_NODE = "suffix" ;
+    /** Admin user dn property tag. */
+	public static final String ADMINDN_NODE = "adminUserDN" ;
+    /** Admin user password property tag. */
+	public static final String ADMINPW_NODE = "adminUserPassword" ;
+    /** Working directory path property tag. */
+	public static final String WDIR_NODE = "workingDirPath" ;
+    /** Entry cache size property tag. */
+	public static final String CACHESZ_NODE = "entryCacheSize" ;
+
+
+    /**
+     * Avalon Configurable interface implementation. Here's an example
+     * configuration:<br>
+     *   <config>
+     *		<backend>
+     * 			<!-- mandatory -->
+     *			<suffix>dc=domain,dc=com</suffix>
+     * 
+     * 			<!-- mandatory -->
+     *			<adminUserDN>cn=admin,dc=domain,dc=com</adminUserDN>
+     * 
+     * 			<!-- mandatory -->
+     *			<adminUserPassword>secret</adminUserPassword>
+     * 
+     * 			<!-- mandatory -->
+     *			<workingDirPath>var/backend0</workingDirPath>
+     * 
+     * 			<!-- optional -->
+     *			<entryCacheSize>100</entryCacheSize>
+     *		</backend>
+     *   </config>
+     *
+     * @param a_config an avalon configuration object for this backend block.
+     * @throws ConfigurationException if the configuration is not correct.
+     */
+    public void configure( Configuration a_config )
+        throws ConfigurationException
+    {
+        Configuration l_suffixConfig = a_config.getChild( SUFFIX_NODE, false ) ;
+        if( l_suffixConfig == null )
+        {
+            throw new ConfigurationException( "Encountered module config with "
+                + "missing <" + SUFFIX_NODE+ "> tag! Cannot process "
+                + "config without unique backend suffix name." ) ;
+        }
+
+        // Got through all the attributes and add it to the suffix multimap
+        Configuration [] l_attributes = l_suffixConfig.getChildren() ;
+        for( int ii = 0 ; ii < l_attributes.length ; ii++ )
+        {
+            String l_name = l_attributes[ii].getAttribute( "name", null ) ;
+            String l_value = l_attributes[ii].getAttribute( "value", null ) ;
+
+            if( getLogger().isDebugEnabled() )
+            {
+                getLogger().debug( "Adding suffix attribute " + l_name
+                    + " with value " + l_value ) ;
+            }
+
+            m_suffixEntry.put( l_name, l_value ) ;
+        }
+
+		if( getLogger().isDebugEnabled() )
+        {
+            getLogger().debug( "Dumping suffix entry:\n" + m_suffixEntry ) ;
+        }
+
+        // Check multimap for the long and short names of 'dn' and complain if
+        // this or 'distinguishedName' keys do not exist.
+        Collection l_dnVals = ( Collection )
+            m_suffixEntry.get( Schema.DN_ATTR ) ;
+		if( l_dnVals == null )
+        {
+            l_dnVals = ( Collection ) m_suffixEntry.get( "dn" ) ;
+        }
+        if( l_dnVals == null )
+        {
+            throw new ConfigurationException( "Encountered module config with a"
+                + " missing attribute for the dn! Cannot process config without"
+                + " unique backend suffix distinguished name." ) ;
+        }
+
+        String l_suffix = null ;
+        try
+        {
+        	l_suffix = ( String ) l_dnVals.iterator().next() ;
+        }
+        catch( NoSuchElementException e )
+        {
+            throw new ConfigurationException( "Encountered module config with a"
+                + " missing value for the dn! Cannot process config without"
+                + " unique backend suffix distinguished name.") ;
+        }
+        if( l_suffix == null || l_suffix.trim().equals( "" ) )
+        {
+            throw new ConfigurationException("Encountered module config with a "
+                + "missing value for the dn! Cannot process config without "
+                + "unique backend suffix distinguished name.") ;
+        }
+
+
+        try
+        {
+            m_schema = m_schemaManager.getSchema( l_suffix ) ;
+
+            if( m_schema == null )
+            {
+                throw new ConfigurationException( "Got a null schema from the "
+                    + "schema manager using the suffix " + l_suffix ) ;
+            }
+            else if( getLogger().isDebugEnabled() )
+            {
+                getLogger().debug( "Got schema for suffix " + l_suffix ) ;
+            }
+
+            m_suffix = m_schema.getNormalizingParser().parse( l_suffix ) ;
+        }
+        catch( InvalidNameException e )
+        {
+            String l_msg = "The module suffix " + l_suffix
+                + " is not syntacticly correct: " ;
+            getLogger().error( l_msg, e ) ;
+            throw new ConfigurationException( l_msg, e ) ;
+        }
+        catch( NameNotFoundException e )
+        {
+            String l_msg = "Schema manager does not recognize this suffix: "
+                + l_suffix ;
+            getLogger().error( l_msg, e ) ;
+            throw new ConfigurationException( l_msg, e ) ;
+        }
+        catch( NamingException e )
+        {
+            String l_msg = "Schema manager does not recognize this suffix: "
+                + l_suffix ;
+            getLogger().error( l_msg, e ) ;
+            throw new ConfigurationException( l_msg, e ) ;
+        }
+
+
+        String l_adminUserDn =
+            a_config.getChild( ADMINDN_NODE ).getValue( null ) ;
+        if( l_adminUserDn == null )
+        {
+            throw new ConfigurationException( "Encountered "
+                + getImplementationName()
+                + " module config for suffix " + m_suffix + " with missing <"
+                + ADMINDN_NODE
+                + "> tag! Cannot process config without this mandatory tag." ) ;
+        }
+
+        try
+        {
+            m_adminUser =
+                m_schema.getNormalizingParser().parse( l_adminUserDn ) ;
+        }
+        catch( NamingException e )
+        {
+            throw new ConfigurationException( "Encountered "
+                + getImplementationName()
+                + " module config for suffix " + m_suffix + " with bad <"
+                + this.ADMINDN_NODE + "> tag! Cannot process config without "
+                + "this mandatory tag being present with a valid DN" ) ;
+        }
+
+		m_adminPassword = a_config.getChild( ADMINPW_NODE ).getValue( null ) ;
+        if( m_adminPassword == null )
+        {
+            throw new ConfigurationException( "Encountered "
+                + getImplementationName()
+                + " module config for suffix " + m_suffix + " with missing <"
+                + ADMINPW_NODE
+                + "> tag! Cannot process config without this mandatory tag." ) ;
+        }
+
+		m_wkdirPath = m_context.getBaseDirectory().getAbsolutePath()
+            + File.separator + a_config.getChild( WDIR_NODE ).getValue( null ) ;
+        File l_dir = new File( m_wkdirPath ) ;
+		if( ! l_dir.exists() )
+        {
+			l_dir.mkdirs() ;
+		}
+
+        if( m_wkdirPath == null )
+        {
+            throw new ConfigurationException( "Encountered "
+                + getImplementationName()
+                + " module config for suffix " + m_suffix + " with missing <"
+                + WDIR_NODE
+                + "> tag! Cannot process config without this mandatory tag." ) ;
+        }
+
+        m_cache.setMaximumSize( a_config.getChild( CACHESZ_NODE ).
+            getValueAsInteger( DEFAULT_ENTRY_CACHESZ ) ) ;
+    }
+
+
+    /**
+     * Looks up the BlockContext to get the base directory for the application.
+     */
+    public void contextualize( Context a_context )
+        throws ContextException
+    {
+        super.contextualize( a_context ) ;
+
+        try
+        {
+        	m_context = ( BlockContext ) a_context ;
+            if( getLogger().isDebugEnabled() )
+            {
+        		getLogger().debug( "Got handle on block context with base "
+                    + "directory "
+                    + m_context.getBaseDirectory().getAbsolutePath() ) ;
+            }
+        }
+        catch( ClassCastException e )
+        {
+			getLogger().debug( "Context is not an instance of BlockContext!" ) ;
+            throw new ContextException(
+                "Context is not an instance of BlockContext!", e ) ;
+        }
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/Cursor.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/Cursor.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,424 @@
+/*
+ * $Id: Cursor.java,v 1.7 2003/03/13 18:26:44 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import java.util.ArrayList ;
+import java.util.Observable ;
+import java.util.Enumeration ;
+
+import javax.naming.NamingException ;
+import javax.naming.NamingEnumeration ;
+
+import org.apache.avalon.framework.logger.Logger ;
+import org.apache.avalon.framework.logger.LogEnabled ;
+import java.util.Iterator;
+
+
+/**
+ * Cursors are automagically closing server-side NamingEnumerations.
+ * 
+ * Their open/closed state changes are comunicated to their Observers which
+ * are usually the backends that created them.
+ * <br><br>
+ * Backend implementors are required to have at least one concrete Cursor
+ * implementation. To do so requires the definition of three protected methods:
+ * <br>
+ * <br>
+ * <code> freeResources: </code>
+ * Cursors are potentially valuble resources to a backend and hence generally
+ * need to free up their own resources when exhausted.  The mechanism if any to
+ * free such resources will be specific to the backend's implementation. Cursor
+ * implementors are hence required to provide a protected <code>freeResources
+ * </code> method which is called whenever <code>close</code> is called on
+ * an open cursor.  Hence, <code>freeResources</code> can only be called once
+ * by calls to <code>close</code>.  The <code>freeResources</code>
+ * implementation should not make calls to the <code>close</code> method.
+ * Cursors automatically close themselves when they detect the consumption of
+ * all elements via calls to NamingEnumeration interfaces.
+ * <br><br>
+ * <code> advance</code>
+ * Backend failures need to propagate forward without breaking the Enumeration
+ * interface contract.  Unfortunately enumerations do not allow generic
+ * exception handling while getting the next element.  To work around this
+ * problem while also supporting NamingEnumeration methods, we require concrete
+ * cursor implementations to define an <code>advance</code> method
+ * implementation.  Rather than place iteration code within the
+ * <code>nextElement</code> implementation which is final and hence cannot be
+ * overridden by subclasses, implementors must use the protected <code>advance
+ * </code> method.  In fact, the <code>nextElement</code>
+ * implementation delegates calls to the <code>advance</code> method by
+ * wrapping the invokation with a BackendException/NamingException try catch
+ * which transduces the BackendExceptions and NamingExceptions into a subclass
+ * of NoSuchElementException (a.k.a a CursorException).
+ * <br><br>
+ * <code> canAdvance</code>
+ * Tests to see if this Cursor can advance will most likely be backend specific
+ * and may in some implementations throw a BackendException.  To facilitate the
+ * automatic close of cursors on list consumption and backend exceptions <code>
+ * hasMoreElement</code> has been defined as a final method that wraps calls
+ * to <code>canAdvance</code>.  Unlike the <code>advance</code> method
+ * backend exceptions are not transduced.
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.7 $
+ * 
+ * @see CursorException
+ * @see NamingEnumeration
+ */
+public abstract class Cursor extends Observable
+        implements          				  // Cursors enumerate records
+            NamingEnumeration,                // Cursors can & should be closed
+            LogEnabled                        // Cursors can be enabled to log
+{
+    public static boolean debug = false ; 
+    /** Tracks this Cursors closed/open state. */
+    private boolean m_isClosed = false ;
+    /** Logger used by this Cursor */
+    private Logger m_logger = null ;
+    /** Cursor Listener List */
+    private ArrayList m_listeners = new ArrayList() ;
+
+
+    /////////////////////////////////////////
+    // LogEnabled Interface Implementation //
+    /////////////////////////////////////////
+
+
+    /**
+     * Sets the logger used by this Cursor which most probably will be set by
+     * its backend (factory).
+     *
+     * @param a_logger the logger to be used by this Cursor.
+     */
+    public void enableLogging(Logger a_logger)
+    {
+        m_logger = a_logger ;
+    }
+
+
+    /**
+     * Gets the logger used by this Cursor which most probably was inherited
+     * from its backend (factory).
+     *
+     * @return the logger used by this Cursor.
+     */
+    protected Logger getLogger()
+    {
+        return m_logger ;
+    }
+
+
+    ////////////////////////////////////////
+    // Closable Interface Implementations //
+    ////////////////////////////////////////
+
+
+    /**
+     * Gets whether this Cursor is currently open or closed.
+     *
+     * @return true if this Cursor is closed, otherwise false.
+     */
+    public final boolean isClosed()
+    {
+        return m_isClosed ;
+    }
+
+
+    /**
+     *
+     */
+    public final void close()
+        throws NamingException
+    {
+        if (!m_isClosed) {
+            m_isClosed = true ;
+            freeResources() ;
+            super.setChanged() ;
+        }
+    }
+
+
+    ///////////////////////////////////////////
+    // Enumeration Interface Implementations //
+    ///////////////////////////////////////////
+
+
+    /**
+     * Wrapper around advance which serves as an exception trap that transduces
+     * BackendExceptions into CursorExceptions (a subclass of the expected RT
+     * NoSuchElementException).
+     *
+     * @warning Invokations within the advance() implementations of subclasses
+     * will result in a never ending chain recursion.
+     * @return the next element in this Cursor.
+     * @side-effect Closes this cursor before throwing any exceptions.
+     */
+    public final Object nextElement()
+    {
+        if (m_isClosed) {
+            throw new CursorException("Cannot advance a closed cursor.") ;
+        }
+
+        Object l_element = null ;
+        try {
+            l_element = advance() ;
+
+            if (l_element == null) {
+                try {
+                    close() ;
+                } catch(NamingException ne) {
+					if( null != m_logger ) {
+						m_logger.error("Failed to close this cursor on null return"
+							+ " from advance():", ne) ;
+					}
+                }
+
+                throw new
+                    CursorException("Cursor advance returned null element!") ;
+            }
+
+            if(m_listeners.size() > 0) {
+                Iterator l_list = m_listeners.iterator() ;
+                CursorEvent l_event = new CursorEvent(this, l_element) ;
+
+                while(l_list.hasNext()) {
+                    CursorListener l_listener = (CursorListener) l_list.next() ;
+                    l_listener.cursorAdvanced(l_event) ;
+                }
+            }
+
+            return l_element ;
+        } catch (BackendException e) {
+			if( null != m_logger ) {
+				m_logger.error("Backing store errors resulted in cursor failure " +
+					"closing cursor:", e) ;
+			}
+
+            try {
+                close() ;
+            } catch(NamingException ne) {
+				if( null != m_logger ) {
+					m_logger.error("Failed to close this cursor on error:", ne) ;
+				}
+            }
+
+            throw new CursorException(e) ;
+        } catch (NamingException ne) {
+			if( null != m_logger ) {
+				m_logger.error("Failed on advance() closing cursor: ", ne) ;
+			}
+
+            try {
+                close() ;
+            } catch(NamingException ne2) {
+				if( null != m_logger ) {
+					m_logger.error("Failed to close this cursor on error:", ne2) ;
+				}
+            }
+
+            throw new CursorException(ne) ;
+        }
+    }
+
+
+    /**
+     * Wrapper around advance which serves as an exception trap that transduces
+     * BackendExceptions into CursorExceptions (a subclass of the expected RT
+     * NoSuchElementException).
+     *
+     * @warning Invokations within the advance() implementations of subclasses
+     * will result in a never ending chain recursion.
+     * @return the next element in this Cursor.
+     * @side-effect Closes this cursor before throwing any exceptions.
+     */
+    public final Object next()
+        throws NamingException
+    {
+        if (m_isClosed) {
+            throw new CursorException("Cannot advance a closed cursor.") ;
+        }
+
+        Object l_element = null ;
+        try {
+            l_element = advance() ;
+
+            if (l_element == null) {
+                close() ;
+                throw new
+                    CursorException("Cursor advance returned null element!") ;
+            }
+
+            return l_element ;
+        } catch (BackendException e) {
+			if( null != m_logger ) {
+				m_logger.error("Backing store errors resulted in cursor failure " +
+					"closing cursor:", e) ;
+			}
+            close() ;
+            throw new CursorException(e) ;
+        }
+    }
+
+
+    /**
+     * Wrapper around canAdvance which serves as means to close this Cursor 
+     * when the enumeration completes or an exception is encountered.
+     *
+     * @warning Invokations within the canAdvance implementations of subclasses
+     * will result in a never ending chain recursion.
+     * @return true if there exists a next element in this Cursor, else false
+     * @side-effect Closes this cursor if canAdvance() returns false or if it
+     * encounters a BackendException on the call to canAdvance().
+     */
+    public final boolean hasMoreElements()
+    {
+        if (m_isClosed) {
+            return false ;
+        }
+
+        try {
+            if (!canAdvance()) {
+                try {
+					if( null != m_logger ) {
+						if(m_logger.isDebugEnabled() && debug) {
+							m_logger.debug("Automagically closing exhausted "
+								+ "cursor.") ;
+						}
+                    }
+
+                    close() ;
+                } catch(NamingException ne) {
+					if( null != m_logger ) {
+						m_logger.error("Failed close upon exhausting cursor.", ne);
+					}
+                }
+
+                return false ;
+            }
+
+            return true ;
+        } catch (BackendException e) {
+			if( null != m_logger ) {
+				m_logger.error("Backend errors closing cursor prematurely", e) ;
+			}
+
+            try {
+                close() ;
+            } catch(NamingException ne) {
+				if( null != m_logger ) {
+					m_logger.error("Failed close upon backend error:", ne) ;
+				}
+            }
+
+            return false ;
+        } catch (NamingException e) {
+			if( null != m_logger ) {
+				m_logger.error("Naming errors closing cursor prematurely", e) ;
+			}
+
+            try {
+                close() ;
+            } catch(NamingException ne) {
+				if( null != m_logger ) {
+					m_logger.error("Failed close upon backend error:", ne) ;
+				}
+            }
+
+            return false ;
+        }
+    }
+
+
+    /**
+     * Wrapper around canAdvance which serves as a means to close this Cursor 
+     * when the enumeration completes or an exception is encountered.
+     *
+     * @warning Invokations within the canAdvance implementations of subclasses
+     * will result in a never ending chain recursion.
+     * @return true if there exists a next element in this Cursor, else false
+     * @side-effect Closes this cursor if canAdvance() returns false or if it
+     * encounters a BackendException on the call to canAdvance().
+     */
+    public final boolean hasMore()
+        throws NamingException
+    {
+        if (m_isClosed) {
+            return false ;
+        }
+
+        try {
+            if (!canAdvance()) {
+				if( null != m_logger ) {
+					if(m_logger.isDebugEnabled() && debug) {
+						m_logger.debug("Automagically closing exhausted cursor.") ;
+					}
+				}
+
+                close() ;
+                return false ;
+            }
+
+            return true ;
+        } catch (BackendException e) {
+			if( null != m_logger ) {
+				m_logger.error("Backend errors closing cursor prematurely", e) ;
+			}
+            close() ;
+            return false ;
+        }
+    }
+
+
+    //////////////////////
+    // Abstract Methods //
+    //////////////////////
+
+
+    /**
+     * Delagate method called by hasMore[Elements] to test if we can move
+     * forward.  Use this method to hold the code that would have normally gone
+     * into the hasMore[Elements] body. Unlike the hasMore[Elements] method the
+     * method allows the backend developer to throw backend specific exceptions
+     * which are eventually transduced into an automatic close of this Cursor.
+     * After closing this Cursor BackendExceptions are not rethrown as runtime
+     * exceptions to maintain [Naming]Enumeration semantics.
+     *
+     * @return true if Cursor can move to next non null entry, false otherwise.
+     * @throws BackendException when a error occurs on the entry backing store.
+     */
+    protected abstract boolean canAdvance()
+        throws BackendException, NamingException ;
+
+
+    /**
+     * Delagate method called by next[Element] to move this Cursor one element
+     * forward.  Use this method to hold the code that would have normally gone
+     * into the next[Element] body. Unlike the next[Element] method this method
+     * allows the backend developer to throw backend specific exceptions.
+     * BackendExceptions (which are not runtime exceptions) are transduced into
+     * CursorExceptions (which are runtime exceptions derived from
+     * NoSuchElementException).
+     *
+     * @return the element pointed to by this Cursor after advancing a step.
+     * @throws BackendException when a error occurs on the entry backing store.
+     */
+    protected abstract Object advance()
+        throws BackendException, NamingException ;
+
+
+    /**
+     * Called only once on cursor close to free up resources held by this
+     * Cursor.
+     */
+    protected abstract void freeResources() ;
+}
+

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/CursorEvent.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/CursorEvent.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,39 @@
+/*
+ * $Id: CursorEvent.java,v 1.2 2003/03/13 18:26:47 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import java.util.EventObject;
+
+
+public class CursorEvent
+	extends EventObject
+{
+	public final Object element ;
+
+    CursorEvent(final Cursor a_cursor, final Object a_element)
+    {
+        super(a_cursor) ;
+
+        element = a_element ;
+    }
+
+
+    Cursor getCursorSource()
+    {
+        return (Cursor) this.source ;
+    }
+
+
+    Object getElement()
+    {
+        return element ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/CursorException.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/CursorException.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,73 @@
+/*
+ * $Id: CursorException.java,v 1.2 2003/03/13 18:26:49 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import java.util.NoSuchElementException ;
+
+
+/**
+ * This exception is thrown when Cursor has no more elements to return.
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.2 $
+ */
+public class CursorException
+    extends NoSuchElementException
+{
+    /** Nested exception if any. */
+    private final Throwable m_error ;
+
+
+    /**
+     * Constructs an Exception with a nested Throwable whose message is
+     * used to compose this exceptions message.
+     *
+     * @param an_exception a backend exception to wrap.
+     */
+    public CursorException(Throwable an_exception)
+    {
+        super(an_exception.toString()) ;
+        this.m_error = an_exception ;
+    }
+
+    /**
+     * Constructs an Exception without a message and without a Throwable.
+     */
+    public CursorException()
+    {
+        super() ;
+        this.m_error = null ;
+    }
+
+    /**
+     * Constructs an Exception with a detailed message.
+     * 
+     * @param a_message The message associated with the exception.
+     */
+    public CursorException(String a_message)
+    {
+        super(a_message) ;
+        this.m_error = null ;
+    }
+
+
+    /**
+     * Checks to see if this exception was due to a backend/database error.
+     *
+     * @return true if a Throwable is wrapped by this exception or false
+     * otherwise.
+     */
+    public boolean isError()
+    {
+        return this.m_error != null ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/CursorListener.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/CursorListener.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,20 @@
+/*
+ * $Id: CursorListener.java,v 1.2 2003/03/13 18:26:50 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import java.util.EventListener ;
+
+
+public interface CursorListener
+    extends EventListener
+{
+	void cursorAdvanced(CursorEvent an_event) ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/EmptyCursor.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/EmptyCursor.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,49 @@
+/*
+ * $Id: EmptyCursor.java,v 1.2 2003/03/13 18:26:51 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import java.util.NoSuchElementException ;
+
+
+/**
+ * An empty cursor that is closed on creation! Used as annon inner class in so
+ * many places I had to create it.
+ * 
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.2 $
+ */
+public class EmptyCursor
+    extends Cursor
+{
+    /**
+     * Closes on creation.
+     */
+    public EmptyCursor()
+    {
+        try { close() ; } catch(Exception e) {} ;
+    }
+
+    /**
+     * Throws NoSuchElementException all the time.
+     */
+    public Object advance() { throw new NoSuchElementException() ; }
+
+    /**
+     * Always returns false.
+     */
+    public boolean canAdvance() { return false ; }
+
+    /**
+     * Does nothing.
+     */
+    public void freeResources() { }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/EnumerationCursor.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/EnumerationCursor.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,61 @@
+/*
+ * $Id: EnumerationCursor.java,v 1.2 2003/03/13 18:26:52 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import java.util.Enumeration ;
+
+
+/**
+ * A cursor using an underlying Enumeration.
+ * 
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.2 $
+ */
+public class EnumerationCursor
+    extends Cursor
+{
+    private final Enumeration m_enumeration ;
+
+    /**
+     * Creates a cursor over an Enumeration.
+     * 
+     * @param a_enumeration the underlying Enumeration this cursor uses.
+     */
+    public EnumerationCursor(Enumeration a_enumeration)
+    {
+        m_enumeration = a_enumeration ;
+    }
+
+
+    /**
+     * Returns enumeration.nextElement()
+     * @return enumeration.nextElement()
+     */
+    public Object advance()
+    {
+        return m_enumeration.nextElement() ;
+    }
+
+
+    /**
+     * Returns enumeration.hasMoreElements()
+     * @return Returns enumeration.hasMoreElements()
+     */
+    public boolean canAdvance()
+    {
+        return m_enumeration.hasMoreElements() ;
+    }
+
+
+    /** Does nothing */
+    public void freeResources() { }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/IteratorCursor.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/IteratorCursor.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,55 @@
+/*
+ * $Id: IteratorCursor.java,v 1.2 2003/03/13 18:26:54 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import java.util.Iterator ;
+
+
+/**
+ * A cursor using an underlying Iterator.
+ * 
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.2 $
+ */
+public class IteratorCursor
+    extends Cursor
+{
+    private final Iterator m_iterator ;
+
+    /**
+     * Creates a cursor over an Iterator.
+     * 
+     * @param a_iterator the underlying iterator this cursor uses.
+     */
+    public IteratorCursor(Iterator a_iterator)
+    {
+        m_iterator = a_iterator ;
+    }
+
+
+    /** Returns iterator.next() */
+    public Object advance()
+    {
+        return m_iterator.next() ;
+    }
+
+
+    /** Returns iterator.hasNext() */
+    public boolean canAdvance()
+    {
+        return m_iterator.hasNext() ;
+    }
+
+
+    /** Does nothing */
+    public void freeResources() { }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/LdapEntry.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/LdapEntry.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,297 @@
+/*
+ * $Id: LdapEntry.java,v 1.8 2003/03/13 18:26:55 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+import java.math.BigInteger ;
+import java.io.Serializable ;
+
+import java.util.Date ;
+import java.util.Collection ;
+
+import javax.naming.Name ;
+import javax.naming.directory.SchemaViolationException ;
+import javax.naming.directory.AttributeModificationException ;
+import javax.naming.directory.InvalidAttributeValueException ;
+import javax.naming.directory.InvalidAttributeIdentifierException ;
+import javax.naming.directory.AttributeInUseException ;
+import java.util.Iterator;
+import org.apache.eve.schema.Schema;
+import javax.naming.NamingException;
+
+
+/**
+ * Internal server side representation of an LDAP entry.  All backend entry
+ * implementations are expected to implement this interface.  The interface
+ * defines standard house keeping attributes used by this LDAP v3
+ * implementation.
+ *
+ * @task We need to explicitly outline some semantics for the various house
+ * keeping constants - like the parent id of the suffix entry should always be
+ * itself etc.
+ * 
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.8 $
+ * @see Backend
+ */
+public interface LdapEntry
+    extends Serializable
+{
+    /**
+     * The identifier of the Distinguished name attribute for this entry.  The
+     * value of this attribute is NOT a normalized DN it is the user provide 
+     * DN.
+     *
+     * @see getEntryDN
+     */
+    String DN_ATTR = Schema.DN_ATTR ;
+    /**
+     * The identifier of the normalized Distinguished name attribute for this
+     * Entry's parent.
+     * 
+     * @see getParentDN
+     */
+    String PARENTDN_ATTR = "parentdn" ;
+    /**
+     * The identifier of the normalized Distinguished name attribute for this
+     * Entry's subschema subentry.
+     * 
+     * @see getSubschemaSubentry
+     */
+    String SUBSCHEMASUBENTRY_ATTR = "subschemasubentry" ;
+    /**
+     * The identifier of the normalized Distinguished name attribute for this
+     * Entry's creator.
+     * 
+     * @see getCreatorsName
+     */
+    String CREATORSNAME_ATTR = "creatorsname" ;
+    /**
+     * The identifier of the normalized Distinguished name attribute for this
+     * Entry's last modifier.
+     * 
+     * @see getModifiersName
+     */
+    String MODIFIERSNAME_ATTR = "modifiersname" ;
+    /** Unique entry identifier attribute's name */
+    String ID_ATTR = "entryid" ;
+    /** Entry's parent id attribute's identifier */
+    String PARENTID_ATTR = Schema.HIERARCHY_ATTR ;
+    /** Number of subordinates attribute's identifier */
+    String NUMSUBORDINATES_ATTR = "numsubordinates" ;
+    /** Create timestamp attribute's identifier */
+    String CREATETIMESTAMP_ATTR = "createtimestamp" ;
+    /** Modifier's timestamp attribute's identifier */
+    String MODIFYTIMESTAMP_ATTR = "modifytimestamp" ;
+
+    Collection attributes() ;
+
+    boolean hasAttribute(String an_attributeName) ;
+
+    /**
+     * Checks whether or not this Entry is a valid entry residing within a
+     * backend.  Entries are validated on successful create() calls.  When
+     * Entry instances are initialized in the java sense via the newEntry()
+     * call on backends, they are in the invalid state.
+     *
+     * @return true if this entry has been persisted to a backend,
+     * false otherwise.
+     */
+    boolean isValid() ;
+
+    /**
+     * Gets this entry's creation timestamp as a Date.
+     *
+     * @return Date representing the creation timestamp of this entry.
+     */
+    Date getCreateTimestamp() ;
+
+    /**
+     * Gets this entry's modification timestamp as a Date.
+     *
+     * @return Date representing the timestamp this entry was last modified.
+     */
+    Date getModifyTimestamp() ;
+
+    /**
+     * Gets the unique distinguished name associated with this entry as it was
+     * supplied during creation without whitespace trimming or character case
+     * conversions.  This version of the DN is kept within the body of this
+     * Entry as an operational attribute so that it could be returned as it was
+     * given to the server w/o normalization effects: case and whitespace will
+     * be entact.
+     *
+     * @return the distinguished name of this entry as a String.
+     */
+    String getEntryDN() ;
+
+    /**
+     * Gets the normalized unique distinguished name associated with this 
+     * entry. This DN unlike the user specified DN accessed via getDN() is not
+     * an operational attribute composing the body of this Entry.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     *
+     * @return the normalized distinguished name of this entry as a String.
+     */
+    Name getNormalizedDN()
+        throws NamingException ;
+
+    Name getUnNormalizedDN()
+        throws NamingException ;
+
+    /**
+     * Gets the normalized unique distinguished name of this Entry's parent.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     *
+     * @return the normalized distinguished name of this Entry's parent.
+     */
+    String getParentDN() ;
+
+    /**
+     * Gets the distinguished name of the creator of this entry.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     * 
+     * @return the distinguished name of the creator.
+     */
+    String getCreatorsName() ;
+
+    /**
+     * Gets the distinguished name of the last modifier of this entry.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     * 
+     * @return the DN of the user to modify this entry last.
+     */
+    String getModifiersName() ;
+
+    Schema getSchema() ;
+
+    /**
+     * Gets the distinguished name of the subschema subentry for this Entry.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     * 
+     * @return String of the subschema subentry distinguished name.
+     */
+    String getSubschemaSubentryDN() ;
+
+    /**
+     * Gets a single valued attribute by name or returns the first value of a
+     * multivalued attribute.
+     *
+     * @param a_attribName the name of the attribute to lookup.
+     * @return an Object value which is either a String or byte [] or null if
+     * the attribute does not exist.
+     */
+    Object getSingleValue(String an_attribName) ;
+
+    /**
+     * Gets a multivalued attribute by name.
+     *
+     * @param a_attribName the name of the attribute to lookup.
+     * @return a Collection or null if no attribute value exists.
+     */
+    Collection getMultiValue(String an_attribName) ;
+
+    /**
+     * Adds a value to this Entry potentially resulting in more than one value
+     * for the attribute/key.
+     * 
+     * @param an_attribName attribute name/key
+     * @param a_value the value to add
+     * @throws InvalidAttributeIdentifierException when an attempt is made to
+     * add to or create an attribute with an invalid attribute identifier.
+     * @throws InvalidAttributeValueException when an attempt is made to add to
+     * an attribute a value that conflicts with the attribute's schema
+     * definition. This could happen, for example, if attempting to add an
+     * attribute with no value when the attribute is required to have at least
+     * one value, or if attempting to add more than one value to a single
+     * valued-attribute, or if attempting to add a value that conflicts with 
+     * the syntax of the attribute.
+     */
+    void addValue(String an_attribName, Object a_value)
+        throws
+        AttributeInUseException,
+        InvalidAttributeValueException,
+        InvalidAttributeIdentifierException ;
+
+    /**
+     * Removes the attribute/value pair in this Entry only without affecting
+     * other values that the attribute may have.
+     *
+     * @param an_attribName attribute name/key
+     * @param a_value the value to remove
+     * @throws AttributeModificationException when an attempt is made to modify
+     * an attribute, its identifier, or its values that conflicts with the
+     * attribute's (schema) definition or the attribute's state.  Also thrown
+     * if the specified attribute name does not exist as a key in this Entry.
+     */
+    void removeValue(String an_attribName, Object a_value)
+        throws InvalidAttributeIdentifierException ;
+
+    /**
+     * Removes the specified set of attribute/value pairs in this Entry.
+     *
+     * @param an_attribName attribute name/key
+     * @param a_valueArray the set of values to remove
+     * @throws AttributeModificationException when an attempt is made to modify
+     * an attribute, its identifier, or its values that conflicts with the
+     * attribute's (schema) definition or the attribute's state.  Also thrown
+     * if the specified attribute name does not exist as a key in this Entry.
+     */
+    void removeValues(String an_attribName, Object [] a_valueArray)
+        throws InvalidAttributeIdentifierException ;
+
+    /**
+     * Removes all the attribute/value pairs in this Entry associated with the
+     * attribute.
+     *
+     * @param an_attribName attribute name/key
+     * @param a_value the value to remove
+     * @throws AttributeModificationException when an attempt is made to modify
+     * an attribute, its identifier, or its values that conflicts with the
+     * attribute's (schema) definition or the attribute's state.  Also thrown
+     * if the specified attribute name does not exist as a key in this Entry.
+     */
+    void removeValues(String an_attribName)
+        throws InvalidAttributeIdentifierException ;
+
+    /**
+     * Checks to see if this LdapEntry is equal to an entry with a Dn.
+     *
+     * @param a_dn the dn to test for equality.
+     * @return true if this entry has a Dn equal to a_dn, false otherwise.
+     */
+    boolean equals(String a_dn)
+        throws NamingException ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/NexusModule.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/NexusModule.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,730 @@
+/*
+ * $Id: NexusModule.java,v 1.26 2003/08/22 21:15:54 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import org.apache.eve.AbstractModule ;
+
+import java.util.Map ;
+import java.util.Date ;
+import java.util.HashMap ;
+import java.util.Iterator ;
+
+import javax.naming.Name ;
+import javax.naming.NameParser ;
+import javax.naming.NamingException ;
+import javax.naming.InvalidNameException ;
+import javax.naming.NameNotFoundException ;
+
+import org.apache.ldap.common.name.LdapName ;
+import org.apache.ldap.common.filter.ExprNode ;
+
+import org.apache.eve.schema.Schema ;
+import org.apache.eve.client.ClientManager ;
+import org.apache.eve.schema.SchemaManager ;
+import org.apache.eve.client.ClientSession ;
+import org.apache.eve.client.ClientManagerSlave ;
+
+import org.apache.avalon.framework.ExceptionUtil ;
+import org.apache.avalon.framework.service.ServiceManager ;
+import org.apache.avalon.framework.service.ServiceException ;
+import org.apache.avalon.framework.configuration.Configuration ;
+import org.apache.avalon.framework.configuration.ConfigurationException ;
+
+
+/**
+ * Default backend nexus or unified backend implementation for the server.
+ * 
+ * @phoenix:block
+ * @phoenix:service name="org.apache.eve.backend.UnifiedBackend"
+ * @phoenix:mx-topic name="backend-nexus"
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.26 $
+ */
+public class NexusModule
+    extends AbstractModule
+    implements UnifiedBackend, ClientManagerSlave
+{
+    /** Config tag for RootDSE definition */
+    public static final String ROOTDSE_TAG = "RootDSE" ;
+    /** Config tag for RootDSE attributes */
+	public static final String ATTRIBUTE_TAG = "attribute" ;
+
+    ClientManager m_clientMan = null ;
+    SchemaManager m_schemaMan = null ;
+    Schema m_schema = null ;
+    RootDSE m_rootDSE = null ;
+
+    /** Map of backend normalized suffix strings to backends */
+    Map m_backends = new HashMap() ;
+    Map m_repListeners = new HashMap() ;
+    Map m_adminUsers = new HashMap() ;
+
+
+    // -------------------------------------------------------
+    // Operational Attribute Management Methods
+    // -------------------------------------------------------
+
+
+    /**
+     * Sets an entries operational attributes based on client session parameters
+     * and whether or not the operation is a create or an update.
+     *
+     * @param an_entry the entry to add or alter the operational attributes of.
+     * @param isCreate true if op attrs are added for create or false for update
+     */
+    private void setOperationalAttributes( LdapEntry an_entry,
+        boolean isCreate ) throws BackendException, NamingException
+    {
+        String l_now = new Date().toGMTString() ;
+        String l_userDn = null ;
+        ClientSession l_session = m_clientMan.getClientSession() ;
+
+        if( null == l_session )
+        {
+            throw new BackendException(
+                "Cannot obtain client session object!" ) ;
+        }
+
+        // Get authenticated principal's Dn
+        l_userDn = l_session.getPrincipal().getDn().toString() ;
+
+        if( isCreate )
+        {
+            if( an_entry.hasAttribute( LdapEntry.CREATETIMESTAMP_ATTR ) )
+            {
+                an_entry.removeValues( LdapEntry.CREATETIMESTAMP_ATTR ) ;
+            }
+
+            if( an_entry.hasAttribute( LdapEntry.CREATORSNAME_ATTR ) )
+            {
+                an_entry.removeValues( LdapEntry.CREATORSNAME_ATTR ) ;
+            }
+
+            an_entry.addValue( LdapEntry.CREATETIMESTAMP_ATTR, l_now ) ;
+            an_entry.addValue( LdapEntry.CREATORSNAME_ATTR, l_userDn ) ;
+        }
+
+        if( an_entry.hasAttribute( LdapEntry.MODIFYTIMESTAMP_ATTR ) )
+        {
+            an_entry.removeValues( LdapEntry.MODIFYTIMESTAMP_ATTR ) ;
+        }
+
+        if( an_entry.hasAttribute( LdapEntry.MODIFIERSNAME_ATTR ) )
+        {
+            an_entry.removeValues( LdapEntry.MODIFIERSNAME_ATTR ) ;
+        }
+
+        an_entry.addValue( LdapEntry.MODIFYTIMESTAMP_ATTR, l_now ) ;
+        an_entry.addValue( LdapEntry.MODIFIERSNAME_ATTR, l_userDn ) ;
+    }
+
+
+    // -------------------------------------------------------
+    // CRUD Method Implementations
+    // -------------------------------------------------------
+
+
+    public void create( LdapEntry an_entry )
+        throws BackendException, NamingException
+    {
+        setOperationalAttributes ( an_entry, true ) ;
+        getBackend( an_entry.getNormalizedDN() ).create( an_entry ) ;
+    }
+
+
+    public LdapEntry newEntry( String a_dn )
+        throws BackendException, NamingException
+    {
+        return getBackend( getNormalizedName( a_dn ) ).newEntry( a_dn ) ;
+    }
+
+
+    public LdapEntry read( Name a_dn )
+        throws BackendException, NamingException
+    {
+        return getBackend(
+            getNormalizedName( a_dn.toString() ) ).read( a_dn ) ;
+    }
+
+
+    public void update(LdapEntry an_entry)
+        throws BackendException, NamingException
+    {
+        setOperationalAttributes (an_entry, false) ;
+        getBackend( an_entry.getNormalizedDN() ).update(an_entry) ;
+    }
+
+
+    public void delete( LdapEntry an_entry )
+        throws BackendException, NamingException
+    {
+        getBackend( an_entry.getNormalizedDN() ).delete( an_entry ) ;
+    }
+
+
+    public LdapEntry getParent(Name a_childDn)
+        throws BackendException, NamingException
+    {
+        return getBackend(a_childDn).getParent(a_childDn) ;
+    }
+
+
+    public boolean hasEntry(Name a_dn)
+        throws BackendException, NamingException
+    {
+        return getBackend(a_dn).hasEntry(a_dn) ;
+    }
+
+
+    public boolean isSuffix(LdapEntry an_entry)
+    {
+        try {
+        	return getBackend(an_entry.getNormalizedDN()).isSuffix(an_entry) ;
+        } catch(NamingException e) {
+            // Should never really be thrown.
+            return false ;
+        }
+    }
+
+
+    public Name getNormalizedName(String a_name)
+        throws NamingException
+    {
+        return m_schema.getNormalizingParser().parse(a_name) ;
+    }
+
+
+    public Name getName(String a_name)
+        throws NamingException
+    {
+        return m_schema.getNameParser().parse(a_name) ;
+    }
+
+
+    /**
+     * V E R Y   I N E F F I C I E N T
+     * 
+     * This could be made much more efficient by hashing all the normalized
+     * admin user names against their backends in a hashmap and doing the
+     * lookup after converting the a_userDn arguement into its canonical form.
+     */
+    public boolean isAdminUser(Name a_userDn)
+        throws NamingException
+    {
+        if(m_adminUsers.containsKey(a_userDn.toString())) {
+            return true ;
+        }
+
+        return false ;
+    }
+
+
+    public RootDSE getRootDSE()
+    {
+        return m_rootDSE ;
+    }
+
+
+    public Cursor listChildren(Name a_parentDn)
+        throws BackendException, NamingException
+    {
+        return getBackend(a_parentDn).listChildren(a_parentDn) ;
+    }
+
+
+    public Cursor search(ExprNode a_searchFilter, Name a_baseDn,
+		  int a_scope)
+        throws BackendException, NamingException
+    {
+        // Base search without a namespace returns the RootDSE within a
+        // SingletonCursor that returns only one entry: the RootDSE.
+        if(a_baseDn.toString().trim().equals("") && a_scope == BASE_SCOPE) {
+            return new SingletonCursor(m_rootDSE) ;
+        }
+
+        return getBackend(a_baseDn).search(a_searchFilter,
+            a_baseDn, a_scope) ;
+    }
+
+
+    /**
+     * Modifies the relative distinguished name (RDN) of an entry without 
+     * changing any parent child relationships.  This call has the side effect 
+     * of altering the distinguished name of descendent entries if they exist.
+     * The boolean argument will optionally remove the existing RDN attribute 
+     * value pair replacing it with the new RDN attribute value pair.  If other
+	 * RDN attribute value pairs exist besides the current RDN they will be 
+     * spared.
+     * 
+     * If the new Rdn and the old Rdn are equal this method does nothing.
+     *
+     * @param an_entry the entry whose RDN is to be modified.
+     * @param a_newRdn the new Rdn that is to replace the current Rdn.
+     * @param a_deleteOldRdn deletes the old Rdn attribute value pair if true.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    public void modifyRdn( LdapEntry an_entry, Name a_newRdn,
+        boolean a_deleteOldRdn ) 
+	    throws BackendException, NamingException
+    {
+        AtomicBackend l_be = getBackend( an_entry.getNormalizedDN() ) ;
+        Name l_dn = new LdapName( an_entry.getEntryDN() ) ;
+        String l_oldRdnStr = l_dn.get( l_dn.size() - 1 ) ;
+        String l_newRdnStr = a_newRdn.get( a_newRdn.size() - 1 ) ;
+        
+        // Set common operational attributes on the entry first
+        setOperationalAttributes( an_entry, false ) ;
+        
+        // Only perform modifyRdn operation if newRdn is not the same as old
+        if ( ! l_oldRdnStr.equals( l_newRdnStr ) ) 
+        {
+            l_be.modifyRdn( an_entry, a_newRdn, a_deleteOldRdn ) ;
+        }
+
+        getBackend(an_entry.getNormalizedDN()).
+            modifyRdn(an_entry, a_newRdn, a_deleteOldRdn) ;
+    }
+
+
+    /**
+     * This overload combines the first two method operations into one.
+     * It changes the Rdn and the parent prefix at the same time while
+	 * recursing name changes to all descendants of the child entry. It
+	 * is obviously more complex than the other two operations alone and
+	 * involves changes to both parent child indices and DN indices.
+     *
+     * @param a_parentEntry the parent the child is to subordinate to.
+     * @param a_childEntry the child to be moved under the parent.
+     * @param a_newRdn the new Rdn that is to replace the current Rdn.
+     * @param a_deleteOldRdn deletes the old Rdn attribute value pair if true.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    public void move( LdapEntry a_parentEntry, LdapEntry a_childEntry,
+	    Name a_newRdn, boolean a_deleteOldRdn )
+	    throws BackendException, NamingException
+    {
+        AtomicBackend l_be = getBackend( a_parentEntry.getNormalizedDN() ) ;
+        Name l_childDn = new LdapName( a_childEntry.getEntryDN() ) ;
+        String l_oldRdnStr = l_childDn.get( l_childDn.size() - 1 ) ;
+        String l_newRdnStr = a_newRdn.get( a_newRdn.size() - 1 ) ;
+        
+        Name l_normalizedChildDn = a_childEntry.getNormalizedDN() ;
+        Name l_normalizedOldParentDn = 
+            l_normalizedChildDn.getPrefix( l_normalizedChildDn.size() - 1 ) ;
+
+        // Set common operational attributes on the entry first
+        setOperationalAttributes( a_childEntry, false ) ;
+
+
+        if ( l_normalizedOldParentDn.equals( a_parentEntry.getNormalizedDN() ) ) 
+        {
+            if ( l_oldRdnStr.equals( l_newRdnStr ) )
+            {
+                /*
+                 * The new and old rdn are the same and the new and old parents
+                 * are the same as well so we do nothing.  Both rdn renames and
+                 * move operations are pointless so we return.
+                 */
+                return ;
+            }
+            
+            /*
+             * The move operation is unnecessary but the rdn change is valid
+             * so we transduce this call to a modifyRdn call on the backend
+             */
+            l_be.modifyRdn( a_childEntry, a_newRdn, a_deleteOldRdn ) ;
+        }
+        else 
+        {
+            if ( l_oldRdnStr.equals( l_newRdnStr ) ) 
+            {
+                /*
+                 * The new and old rdn are the same so we convert this to a 
+                 * simple move operation.
+                 */
+                l_be.move( a_parentEntry, a_childEntry ) ;
+            } 
+            else 
+            {
+                // Both the move and the rdn change are necessary here!
+                l_be.move( a_parentEntry, a_childEntry, a_newRdn, 
+                    a_deleteOldRdn ) ;
+            }
+        }
+    }
+
+
+    /**
+     * Moves a child entry without changing the RDN under a new parent entry.  
+     * This effects the parent child relationship between the parent entry and 
+     * the child entry.  The index for the child mapping it to the current 
+     * parent is destroyed and a new index mapping it to the new parent is 
+     * created.  As a side effect the name of the child entry and all its 
+     * descendants will reflect the move within the DIT to a new parent.  The 
+     * old parent prefix to the distinguished names of the child and its 
+     * descendents will be replaced by the new parent DN prefix.
+     * 
+     * If the new parent is the same as the old parent this call does nothing
+     * since the operation is pointless.
+     *
+     * @param a_parentEntry the parent the child is to subordinate to.
+     * @param a_childEntry the child to be moved under the parent.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    public void move( LdapEntry a_parentEntry, LdapEntry a_childEntry )
+	    throws BackendException, NamingException
+    {
+        AtomicBackend l_be = getBackend( a_parentEntry.getNormalizedDN() ) ;
+        Name l_dn = a_childEntry.getNormalizedDN() ;
+        Name l_oldParentDn = l_dn.getPrefix( l_dn.size() - 1 ) ;
+        
+        // Fires ModifyProtocolEvent
+        setOperationalAttributes( a_childEntry, false ) ;
+        
+        // Only perform operation if new parent dn is different from the old 
+        if ( ! a_parentEntry.getNormalizedDN().equals( l_oldParentDn ) )
+        {
+            l_be.move( a_parentEntry, a_childEntry ) ;
+        }
+    }
+
+
+    //////////////////////////////////////////////
+    // UnifiedBackend Interface Implementations //
+    //////////////////////////////////////////////
+
+
+    /**
+     * Gets the suffix of the backend responsible for an entry using the entry's
+     * distinguished name.
+     *
+     * @phoenix:mx-operation
+     * @phoenix:mx-description Gets the suffix of the backend storing an entry
+     * by its DN.
+     */
+    public String getBackendSuffixForEntry(String a_dn)
+        throws InvalidNameException, NameNotFoundException
+    {
+        StringBuffer l_buf = new StringBuffer() ;
+
+        l_buf.append("Repsonsible backend module suffix for entry ") ;
+        l_buf.append(a_dn).append(':').append("\n") ;
+	    l_buf.append("=================================") ;
+	    l_buf.append("=================================\n") ;
+	    l_buf.append("\n\n") ;
+
+        try {
+            Name l_dn = getNormalizedName (a_dn) ;
+            BackendModule l_be = (BackendModule) getBackend(l_dn) ;
+            l_buf.append(l_be.getImplementationName()).append("\n") ;
+            l_buf.append(l_be.getSuffix()).append("\n") ;
+        } catch(Throwable t) {
+            l_buf.append(ExceptionUtil.printStackTrace(t)) ;
+        }
+
+        return l_buf.toString() ;
+    }
+
+
+    /**
+     * Gets the most significant Dn that exists within the server and hence can
+     * be matched to an actual entry.
+     *
+     * @param a_dn to use for the matching test.
+     * @return the matching portion of a_dn, or the valid empty string dn if no
+     * match was found.
+     */
+    public Name getMatchedDn( Name a_dn )
+        throws NamingException, BackendException
+    {
+		LdapName l_suffix ;
+
+        // Don't try to match for the empty dn which matches by default if we
+        // try to match it in hasEntry then we will get a failure to find an
+        // atomic backend for it.
+        if( a_dn.size() == 0 )
+        {
+            return a_dn ;
+        }
+        // Return if a_dn itself is matched/exists.
+        else if( hasEntry( a_dn ) )
+        {
+            return a_dn ;
+        }
+
+        // Otherwise we start removing most significant components from the head
+        // of it looking for a suffix dn that matches until the "" dn results
+        l_suffix = ( LdapName ) a_dn.clone() ;
+		while( l_suffix.size() > 0 )
+        {
+			l_suffix.remove( l_suffix.size() - 1 ) ;
+
+            // Short loop on a match
+            if( hasEntry( l_suffix ) )
+            {
+                return l_suffix ;
+            }
+		}
+
+        // Should be the empty string distinguished name
+        return l_suffix ;
+    }
+
+
+    /**
+     * Gets the backend associated with a distinguished name if the name
+     * resolves to a suffix.
+     *
+     * @param a_dn the distinguished name to resolve to a backend
+     */
+    public AtomicBackend getBackend( Name a_dn )
+        throws NamingException
+    {
+        Name l_suffix = null ;
+        Iterator l_list = null ;
+        AtomicBackend l_backend = null ;
+
+        if( ! hasStarted() )
+        {
+            throw new IllegalStateException( "Module has not started!" ) ;
+        }
+
+        if( getLogger().isDebugEnabled() )
+        {
+            getLogger().debug("" //ProtocolModule.getMessageKey()
+                + " - NexusModule.getBackend(): Request for DN " + a_dn) ;
+        }
+
+		// First lets just see if l_dn is a suffix to begin with using a cheap
+        // lookup in the normalized suffix String to backend Hash map.
+	    if( m_backends.containsKey( a_dn.toString() ) )
+        {
+            BackendModule l_be =
+                ( BackendModule ) m_backends.get( a_dn.toString() ) ;
+
+            if( getLogger().isDebugEnabled() )
+            {
+                getLogger().debug("" //ProtocolModule.getMessageKey()
+                    + " - NexusModule.getBackend(): Request for DN " + a_dn
+                    + " returning " + l_be.getImplementationName()
+                    + " with suffix " + l_be.getSuffix() ) ;
+            }
+
+            return l_be ;
+        }
+
+        // So l_dn
+		l_list = listBackends() ;
+        while( l_list.hasNext() )
+        {
+	        l_backend = ( AtomicBackend ) l_list.next() ;
+            l_suffix = l_backend.getSuffix() ;
+            if( a_dn.startsWith( l_suffix ) )
+            {
+                if( getLogger().isDebugEnabled() )
+                {
+                    getLogger().debug( "" //ProtocolModule.getMessageKey()
+                        + " - NexusModule.getBackend(): returning backend "
+                        + l_suffix ) ;
+                }
+                return l_backend ;
+            }
+        }
+
+        // After this point we are screwed!
+        if( getLogger().isDebugEnabled() )
+        {
+            getLogger().debug( "Valid name " + a_dn + " not found under nexus "
+                + "backends.  Throwing IllegalArgumentException!" ) ;
+        }
+
+        throw new IllegalArgumentException( a_dn + " not a valid namespace - "
+            + "could not find matching backend!" ) ;
+    }
+
+
+    public NameParser getNameParser()
+    {
+        return m_schema.getNameParser() ;
+    }
+
+
+    public NameParser getNormalizingParser()
+    {
+        return m_schema.getNormalizingParser() ;
+    }
+
+
+    public NameParser getNormalizingParser(Name a_name)
+        throws NamingException
+    {
+        Schema l_schema = this.getSchema(a_name) ;
+        if(l_schema != null) {
+            return l_schema.getNormalizingParser() ;
+        }
+
+        return null ;
+    }
+
+
+    /**
+     * JMX intended operation to list registered running backends this nexus
+     * currently has attacked to it.
+     * 
+     * @phoenix:mx-operation
+     * @phoenix:mx-description Lists all atomic backends registered with the
+     * nexus (or system unified backend).
+     */
+    public String getRegisteredBackends()
+    {
+		StringBuffer l_buf = new StringBuffer() ;
+
+		l_buf.append("Nexus attached backend modules:").append("\n") ;
+		l_buf.append("=================================") ;
+		l_buf.append("=================================\n") ;
+		l_buf.append("\n\n") ;
+
+		Iterator l_list = listBackends() ;
+       	while(l_list.hasNext()) {
+        	BackendModule l_backend = (BackendModule) l_list.next() ;
+
+            l_buf.append("Implementation Name:\t") ;
+            l_buf.append(l_backend.getImplementationName()).append("\n") ;
+
+            l_buf.append("Implementation Class Name:\t") ;
+            l_buf.append(l_backend.getImplementationClassName()).append("\n") ;
+
+            l_buf.append("Suffix Serviced:\t") ;
+            l_buf.append(l_backend.getSuffix()).append("\n\n\n") ;
+       }
+
+       return l_buf.toString() ;
+    }
+
+
+    public Iterator listBackends()
+    {
+        return m_backends.values().iterator() ;
+    }
+
+
+    public Iterator listSuffixes()
+    {
+        return m_backends.keySet().iterator() ;
+    }
+
+
+    public void register( AtomicBackend a_backend )
+    {
+        // Dynamically register client manager with backends added on fly.
+        a_backend.registerClientManager( m_clientMan ) ;
+        m_backends.put( a_backend.getSuffix().toString(), a_backend ) ;
+        m_adminUsers.put( a_backend.getAdminUserDN().toString(), a_backend ) ;
+    }
+
+
+    public void unregister(AtomicBackend a_backend)
+    {
+        m_backends.remove(a_backend.getSuffix().toString()) ;
+        m_adminUsers.remove(a_backend.getAdminUserDN().toString()) ;
+    }
+
+
+    ///////////////////////////////////////
+    // Life-cycle Method Implementations //
+    ///////////////////////////////////////
+
+
+    /**
+     * We use this to get a handle on the schema manager.
+     * 
+     * @phoenix:dependency name="org.apache.eve.schema.SchemaManager"
+     * @param a_manager the service manager that we get a BackendManager from
+     */
+    public void service(ServiceManager a_manager)
+        throws ServiceException
+    {
+        m_schemaMan =
+            (SchemaManager) a_manager.lookup(SchemaManager.ROLE) ;
+        m_schema = m_schemaMan.getCompleteSchema() ;
+    }
+
+
+    public void configure(Configuration a_config)
+        throws ConfigurationException
+    {
+        m_rootDSE = new RootDSE(m_schema) ;
+
+        try {
+			Configuration l_dseConfig = a_config.getChild(ROOTDSE_TAG, false) ;
+			if(l_dseConfig == null) {
+				String l_errmsg = "The " + ROOTDSE_TAG + " does not exist in "
+					+ "the configuration section for the NexusModule. Cannot "
+					+ "continue configuration without it!" ;
+				throw new ConfigurationException(l_errmsg) ;
+			}
+	
+			Configuration [] l_attributes = l_dseConfig.getChildren(ATTRIBUTE_TAG) ;
+			for(int ii = 0 ; ii < l_attributes.length ; ii++ ) {
+				String l_name = l_attributes[ii].getAttribute("name", null) ;
+				String l_value = l_attributes[ii].getAttribute("value", null) ;
+				m_rootDSE.addValue(l_name, l_value) ;
+			}
+        } catch(Throwable e) {
+            String l_errmsg = "Encountered error while trying to construct the"
+                + " RootDSE during the NexusModule's configuration stage." ;
+            throw new ConfigurationException(l_errmsg, e) ;
+        }
+    }
+
+
+    public String getImplementationRole()
+    {
+        return ROLE ;
+    }
+
+
+    public String getImplementationName()
+    {
+        return "Unified Backend Nexus" ;
+    }
+
+
+    public String getImplementationClassName()
+    {
+        return this.getClass().getName() ;
+    }
+
+
+    public void registerClientManager(ClientManager a_manager)
+    {
+        m_clientMan = a_manager ;
+    }
+
+
+    public Schema getSchema(Name a_dn)
+        throws NamingException
+    {
+        BackendModule l_be = (BackendModule) getBackend(a_dn) ;
+		return l_be.getSchema() ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/NormalizerComponent.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/ReadOnlyException.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/ReadOnlyException.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,35 @@
+/*
+ * $Id: ReadOnlyException.java,v 1.4 2003/07/29 22:08:02 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import javax.naming.OperationNotSupportedException ;
+
+
+/**
+ * This exception is thrown when write operations are attempted against a
+ * Backend configured to be read-only.
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.4 $
+ */
+public class ReadOnlyException
+    extends OperationNotSupportedException
+{
+    /**
+     * Constructs an Exception with a detailed message.
+     * @param a_message The message associated with the exception.
+     */
+    public ReadOnlyException(String a_message)
+    {
+        super(a_message) ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/RootDSE.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/RootDSE.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,414 @@
+/*
+ * $Id: RootDSE.java,v 1.3 2003/03/13 18:27:00 akarasulu Exp $
+ * $Prologue$
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import java.util.Date ;
+import java.util.Collection ;
+
+import javax.naming.Name ;
+import javax.naming.NamingException ;
+import javax.naming.directory.AttributeInUseException ;
+import javax.naming.directory.InvalidAttributeValueException ;
+import javax.naming.directory.InvalidAttributeIdentifierException ;
+
+import org.apache.commons.collections.MultiHashMap ;
+
+import org.apache.ldap.common.name.LdapName ;
+import org.apache.eve.schema.Schema ;
+
+
+/**
+ * An entry implementation for the Berkeley backend.
+ * 
+ * @task No schema checking going on right now - but this needs to change!
+ * @task Also we need to make sure that we are storing dates in the apropriate
+ * fashion
+ */
+public class RootDSE
+    extends MultiHashMap
+	implements LdapEntry
+{
+    private final Schema m_schema ;
+    private final Name m_dn = new LdapName() ;
+    private boolean m_isValid = false ;
+
+
+    RootDSE(Schema a_schema)
+    {
+        m_schema = a_schema ;
+    }
+
+
+    public Collection attributes()
+    {
+        return this.keySet() ;
+    }
+
+
+    public Schema getSchema()
+    {
+        return m_schema ;
+    }
+
+
+	public boolean equals(String a_dn)
+        throws NamingException
+    {
+        if(a_dn == null) {
+            return false ;
+        }
+
+        return a_dn.trim().equals("") ;
+    }
+
+
+    public boolean hasAttribute(String an_attributeName)
+    {
+        return containsKey(an_attributeName) ;
+    }
+
+
+    public boolean hasAttributeValuePair(String an_attribute, Object a_value)
+    {
+        if(containsKey(an_attribute)) {
+            Collection l_collection = getMultiValue(an_attribute) ;
+
+            if(null == l_collection && null == a_value) {
+                return true ;
+            } else if(null == l_collection || null == a_value) {
+                return false ;
+            } else if(l_collection.contains(a_value)) {
+                return true ;
+            } else {
+                return false ;
+            }
+        } else {
+	    return false ;
+        }
+    }
+
+
+    /**
+     * Gets this entry's creation timestamp as a Date.
+     *
+     * @return Date representing the creation timestamp of this entry.
+     */
+    public Date getCreateTimestamp()
+    {
+	return new Date((String) getSingleValue(CREATETIMESTAMP_ATTR)) ;
+    }
+
+
+    /**
+     * Gets the distinguished name of the creator of this entry.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     * 
+     * @return the distinguished name of the creator.
+     */
+    public String getCreatorsName()
+    {
+        return (String) getSingleValue(CREATORSNAME_ATTR) ;
+    }
+
+
+    /**
+     * Gets the unique distinguished name associated with this entry as it was
+     * supplied during creation without whitespace trimming or character case
+     * conversions.  This version of the DN is kept within the body of this
+     * Entry as an operational attribute so that it could be returned as it was
+     * given to the server w/o normalization effects: case and whitespace will
+     * be entact.
+     *
+     * @return the distinguished name of this entry as a String.
+     */
+    public String getEntryDN()
+    {
+        return (String) getSingleValue(DN_ATTR) ;
+    }
+
+
+    /**
+     * Gets the distinguished name of the last modifier of this entry.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     * 
+     * @return the DN of the user to modify this entry last.
+     */
+    public String getModifiersName()
+    {
+        return (String) getSingleValue(MODIFIERSNAME_ATTR) ;
+    }
+
+
+    /**
+     * Gets this entry's modification timestamp as a Date.
+     *
+     * @return Date representing the timestamp this entry was last modified.
+     */
+    public Date getModifyTimestamp()
+    {
+        return new Date((String)
+            getSingleValue(MODIFYTIMESTAMP_ATTR)) ;
+    }
+
+
+    /**
+     * Gets the normalized unique distinguished name associated with this 
+     * entry. This DN unlike the user specified DN accessed via getDN() is not
+     * an operational attribute composing the body of this Entry.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     *
+     * @return the normalized distinguished name of this entry as a String.
+     */
+    public Name getNormalizedDN()
+        throws NamingException
+    {
+        return m_dn ;
+    }
+
+
+    public Name getUnNormalizedDN()
+        throws NamingException
+    {
+        return m_dn ;
+    }
+
+
+    /**
+     * Gets the normalized unique distinguished name of this Entry's parent.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     *
+     * @return the normalized distinguished name of this Entry's parent.
+     */
+    public String getParentDN()
+    {
+        return (String) getSingleValue(PARENTDN_ATTR) ;
+    }
+
+
+    /**
+     * Gets the distinguished name of the subschema subentry for this Entry.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     * 
+     * @return String of the subschema subentry distinguished name.
+     */
+    public String getSubschemaSubentryDN()
+    {
+        return (String) getSingleValue(SUBSCHEMASUBENTRY_ATTR) ;
+    }
+
+
+    /**
+     * Checks whether or not this Entry is a valid entry residing within a
+     * backend.  Entries are validated on successful create() calls.  When
+     * Entry instances are initialized in the java sense via the newEntry()
+     * call on backends, they are in the invalid state.
+     *
+     * @return true if this entry has been persisted to a backend,
+     * false otherwise.
+     */
+    public boolean isValid()
+    {
+        return m_isValid ;
+    }
+
+
+    /**
+     * Gets a multivalued attribute by name.
+     *
+     * @param an_attribName the name of the attribute to lookup.
+     * @return a Collection or null if no attribute value exists.
+     */
+    public Collection getMultiValue(String an_attribName)
+    {
+        return (Collection) get(an_attribName) ;
+    }
+
+
+    /**
+     * Gets a single valued attribute by name or returns the first value of a
+     * multivalued attribute.
+     *
+     * @param an_attribName the name of the attribute to lookup.
+     * @return an Object value which is either a String or byte [] or null if
+     * the attribute does not exist.
+     */
+    public Object getSingleValue(String an_attribName)
+    {
+        Collection l_col = (Collection) get(an_attribName) ;
+
+		if(null == l_col || l_col.isEmpty()) {
+            return null ;
+        }
+
+        return l_col.iterator().next() ;
+    }
+
+
+    /**
+     * Adds a value to this Entry potentially resulting in more than one value
+     * for the attribute/key.
+     * 
+     * @param an_attribName attribute name/key
+     * @param a_value the value to add
+     * @throws InvalidAttributeIdentifierException when an attempt is made to
+     * add to or create an attribute with an invalid attribute identifier.
+     * @throws InvalidAttributeValueException when an attempt is made to add to
+     * an attribute a value that conflicts with the attribute's schema
+     * definition. This could happen, for example, if attempting to add an
+     * attribute with no value when the attribute is required to have at least
+     * one value, or if attempting to add more than one value to a single
+     * valued-attribute, or if attempting to add a value that conflicts with 
+     * the syntax of the attribute.
+     */
+    public void addValue(String an_attribName, Object a_value)
+        throws
+        AttributeInUseException,
+        InvalidAttributeValueException,
+        InvalidAttributeIdentifierException
+    {
+	    if(null == a_value || null == an_attribName) {
+            return ;
+        }
+
+        if(!m_schema.hasAttribute(an_attribName)) {
+            throw new InvalidAttributeIdentifierException(an_attribName +
+                " is not a valid schema recognized attribute name.") ;
+        }
+
+		if(containsKey(an_attribName) &&
+            m_schema.isSingleValue(an_attribName))
+        {
+            throw new AttributeInUseException("A key for attribute "
+                + an_attribName + " already exists!") ;
+        }
+
+
+		String l_value = null ;
+
+		if(a_value.getClass().isArray()) {
+			l_value = new String((byte []) a_value) ;
+		} else {
+			l_value = (String) a_value ;
+		}
+
+		if(l_value.trim().equals("")) {
+			return ;
+		}
+
+		if(!m_schema.isValidSyntax(an_attribName, l_value)) {
+			throw new InvalidAttributeValueException("'" + l_value
+				+ "' does not comply with the syntax for attribute "
+				+ an_attribName) ;
+		}
+
+		put(an_attribName, l_value) ;
+    }
+
+
+    /**
+     * Removes the attribute/value pair in this Entry only without affecting
+     * other values that the attribute may have.
+     *
+     * @param an_attribName attribute name/key
+     * @param a_value the value to remove
+     * @throws InvalidAttributeIdentifierException when an attempt is made to modify
+     * an attribute, its identifier, or its values that conflicts with the
+     * attribute's (schema) definition or the attribute's state.  Also thrown
+     * if the specified attribute name does not exist as a key in this Entry.
+     */
+    public void removeValue(String an_attribName, Object a_value)
+        throws InvalidAttributeIdentifierException
+    {
+        if(!m_schema.hasAttribute(an_attribName)) {
+            throw new InvalidAttributeIdentifierException(an_attribName +
+                " is not a valid schema recognized attribute name.") ;
+        }
+
+		remove(an_attribName, a_value) ;
+    }
+
+
+    /**
+     * Removes all the attribute/value pairs in this Entry associated with the
+     * attribute.
+     *
+     * @param an_attribName attribute name/key
+     * @param an_attribName the value to remove
+     * @throws InvalidAttributeIdentifierException when an attempt is made to modify
+     * an attribute, its identifier, or its values that conflicts with the
+     * attribute's (schema) definition or the attribute's state.  Also thrown
+     * if the specified attribute name does not exist as a key in this Entry.
+     */
+    public void removeValues(String an_attribName)
+        throws InvalidAttributeIdentifierException
+    {
+        if(!m_schema.hasAttribute(an_attribName)) {
+            throw new InvalidAttributeIdentifierException(an_attribName +
+                " is not a valid schema recognized attribute name.") ;
+        }
+
+        if(!containsKey(an_attribName)) {
+            return ;
+        }
+
+		remove(an_attribName) ;
+    }
+
+
+    /**
+     * Removes the specified set of attribute/value pairs in this Entry.
+     *
+     * @param an_attribName attribute name/key
+     * @param a_valueArray the set of values to remove
+     * @throws InvalidAttributeIdentifierException when an attempt is made to modify
+     * an attribute, its identifier, or its values that conflicts with the
+     * attribute's (schema) definition or the attribute's state.  Also thrown
+     * if the specified attribute name does not exist as a key in this Entry.
+     */
+    public void removeValues(String an_attribName, Object [] a_valueArray)
+        throws InvalidAttributeIdentifierException
+    {
+        if(!m_schema.hasAttribute(an_attribName)) {
+            throw new InvalidAttributeIdentifierException(an_attribName +
+                " is not a valid schema recognized attribute name.") ;
+        }
+
+		for(int ii = 0; ii < a_valueArray.length; ii++) {
+			remove(an_attribName, a_valueArray[ii]) ;
+		}
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/SingletonCursor.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/SingletonCursor.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,55 @@
+/*
+ * $Id: SingletonCursor.java,v 1.2 2003/03/13 18:27:01 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import java.util.NoSuchElementException ;
+
+
+/**
+ * An cursor that returns only one element! Used as annon inner class in so
+ * many places I had to create this one as well.
+ * 
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.2 $
+ */
+public class SingletonCursor
+    extends Cursor
+{
+    private final Object m_singleton ;
+
+    public SingletonCursor(Object a_singleton)
+    {
+        m_singleton = a_singleton ;
+    }
+
+    /**
+     * Throws NoSuchElementException all the time.
+     */
+    public Object advance()
+    {
+        try { close() ; } catch(Exception e) {}
+        return m_singleton ;
+    }
+
+    /**
+     * Always returns false.
+     */
+    public boolean canAdvance()
+    {
+        return !isClosed() ;
+    }
+
+    /**
+     * Does nothing.
+     */
+    public void freeResources() { }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/UnifiedBackend.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/UnifiedBackend.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,126 @@
+/*
+ * $Id: UnifiedBackend.java,v 1.13 2003/08/22 21:15:54 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend ;
+
+
+import java.util.Iterator ;
+
+import javax.naming.Name ;
+import javax.naming.NameParser ;
+import javax.naming.NamingException ;
+import javax.naming.InvalidNameException ;
+import javax.naming.NameNotFoundException ;
+import javax.naming.ldap.LdapContext ;
+
+import org.apache.eve.schema.Schema ;
+
+
+/**
+ * Represents a composite backend nexus that forwards backend requests to the
+ * apropriate Backends registered with it.  UnifiedBackends are used to
+ * implement a name switch to get to the respective backend if one exists.
+ *
+ * Effectively, the Unified backend is a specific router interface in a system
+ * implementing the Router, (a.k.a. Request Router or Multiplexer) design
+ * pattern.  AtomicBackends are the channels which are added and removed using
+ * the register and unregister methods of the UnifiedBackend.  The routed
+ * messages are AtomicBackend interface calls.
+ */
+public interface UnifiedBackend
+	extends Backend
+{
+    /**
+     * Role of this service interface as mandated by the avalon framework.
+     */
+    public static final String ROLE = UnifiedBackend.class.getName() ;
+
+
+    /**
+     * Get's a Backend module by checking to see if a distinguished name
+     * contains the suffix of a backend as a prefix.  If no backend can be
+     * found for a valid DN an IllegalArgumentException is thrown or an
+     * InvalidNameException is thrown if the DN is not syntactically correct.
+     *
+     * @param a_dn the distinguished name potentially contained by the backend.
+     * @return the Backend that would contain the entry if it were to exist.
+     * @throws InvalidNameException if a_dn is syntactically incorrect.
+     * @throws NameNotFoundException if no backend can house the
+     * hypothetical entry - it is not within the namespace of the federated
+     * backends.
+     * @deprecated we would like to deprecate this method because we would no
+     * longer like to expose it to the outside world which could misuse it
+     * without providing the required dn in normalized form.
+     */
+    AtomicBackend getBackend(Name a_dn)
+        throws NamingException ;
+
+
+    /**
+     * Gets the most significant Dn that exists within the server and hence can
+     * be matched to an actual entry.
+     *
+     * @param a_dn to use for the matching test.
+     * @return the matching portion of a_dn, or the valid empty string dn if no
+     * match was found.
+     */
+    Name getMatchedDn( Name a_dn )
+        throws NamingException, BackendException ;
+
+    /**
+     * Gets an iteration over the Backends managed by this BackendManager.
+     *
+     * @return the iteration over Backend instances.
+     */
+    Iterator listBackends() ;
+
+    /**
+     * Gets an iteration over the suffixes of the Backends managed by this
+     * UnifiedBackend.
+     *
+     * @return the iteration over Backend suffix names as Strings.
+     */
+    Iterator listSuffixes() ;
+
+    Schema getSchema(Name a_dn)
+        throws NamingException ;
+
+    NameParser getNormalizingParser() ;
+
+    NameParser getNameParser() ;
+
+    NameParser getNormalizingParser(Name a_dn)
+        throws NamingException ;
+
+    Name getNormalizedName(String a_name)
+        throws NamingException ;
+
+    Name getName(String a_name)
+        throws NamingException ;
+
+	RootDSE getRootDSE() ;
+
+	/**
+     * Registers a Backend with this BackendManager.  Called for each Backend
+     * implementation after it is started.  This is the only way it will receive
+     * requests from the protocol server.
+     *
+     * @param a_backend Backend component to register with this manager.
+     */
+    void register(AtomicBackend a_backend) ;
+
+    /**
+     * Unregisters a Backend with this BackendManager.  Called for each
+     * registered Backend right befor it is to be stopped.  This prevents
+     * protocol server requests from reaching the Backend.
+     *
+     * @param a_backend Backend component to unregister with this manager.
+     */
+    void unregister(AtomicBackend a_backend) ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/backendhowto.html
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/backendhowto.html	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title>Backend HOWTO</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF" text="#000000">
+Work in progress . . . 
+</body>
+</html>

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/contract.html
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/contract.html	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,237 @@
+<html>
+<head>
+<title>Server Backend Contract</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF" text="#000000">
+<p><font color="#999999">$Id: contract.html,v 1.1.1.1 2002/11/13 23:46:12 akarasulu Exp $</font> </p>
+<h2 align="center">Server Backend Contract</h2>
+<p>The Server Backend Contract is defined syntactically by the APIs presented 
+  in the backend package. Semantic contracts are also required to make sure that 
+  Backends operate as expected and as efficiently as possible.</p>
+<p>&nbsp;</p>
+<h3 align="center">Syntax Overview</h3>
+<p>Backends are interfaces to an entry backing store. Backends provide create, 
+  read, update and delete (CRUD) operations, against the store. Bulk retreivals 
+  using filtered searches and heirarchical (parent child) relationships must also 
+  be provided. </p>
+<p>Backends are the factories for BackendArtifacts. Together, the Backend interface 
+  and these artifacts specify how entries and their attributes are accessed, created 
+  and modified both individually and in bulk.</p>
+<p>&nbsp;</p>
+<h4>CRUD &amp; Entry Life-Cycle Operations</h4>
+<p>The operations used to manipulate entries use the <b>CRUD</b> model which dictates 
+  the Entry life-cycle. These Backend interface methods are straight forward:</p>
+<ul>
+  <li><code>newEntry(String a_dn):Entry</code></li>
+  <li><font color="#FF0000"><code><b><u>c</u></b></code></font><code>reate(Entry 
+    an_entry):void</code></li>
+  <li><code><b><u><font color="#FF0000">r</font></u></b>ead(String a_dn):Entry</code></li>
+  <li><code><b><u><font color="#FF0000">u</font></u></b>pdate(Entry an_entry):void</code></li>
+  <li><code><b><u><font color="#FF0000">d</font></u></b>elete(Entry an_entry):void</code></li>
+</ul>
+<p>Perhaps the only method worthy of explanation is <code>newEntry</code>. This 
+  method constructs a new empty and invalid Entry instance. Valid entries exist 
+  within the Backend's backing store and invalid entries do not: they have been 
+  instantiated yet <code>create</code> has not been called on the Entry. Backends 
+  are Entry factories in this respect. An invalid Entry merely contains the user 
+  provide DN string as an operational attribute and the <a href="dnnormalization.html">normalized</a> 
+  string stored as a property of the class. These <b>CRUD</b> methods do the obvious 
+  once the ball is set in motion. Newly instantiated entries are altered via Entry 
+  interface methods and added to the Backend using the <code>create</code> method. 
+  Once created the Entry can have attributes manipulated and can be used to make 
+  successful calls to <code>delete</code>, and <code>update</code> (presuming 
+  schema constraints are not violated). Calls to <code>read</code> will return 
+  created and updated entries. These interfaces are simple and straight forward. 
+  The rest of the Backend interface methods can be categorized as non-<b>CRUD</b> 
+  methods belonging to the set of entry checker methods, hierarchical operations, 
+  or search operations:</p>
+<p>Entry Test Methods:</p>
+<ul>
+  <li><code>hasEntry(String a_dn):boolean</code></li>
+  <li><code>isSuffix(Entry an_entry):boolean</code></li>
+</ul>
+<p>Hierarchical Operations:</p>
+<ul>
+  <li><code>getParent(String a_childDN):Entry</code></li>
+  <li><code>listChildren(String a_parentDN):Cursor</code></li>
+</ul>
+<p>Search Operations:</p>
+<ul>
+  <li><code>search(String a_base, String a_filter, SearchControls a_ctls):Cursor</code></li>
+</ul>
+<p>The Backend is responsible for enforcing Schema constraints. Separate Schema 
+  interfaces dictate the interaction between Backend and Schema subsystems. Hence 
+  schema based attribute normalization and schema based objectclass constraint 
+  checking are delegated to the Schema subsystem by the Backend. This dependency 
+  is the source of the various kinds of NamingExceptions thrown by most of the 
+  methods on the Backend interface. A Backend simply let's these exceptions pass 
+  through.</p>
+<p>&nbsp;</p>
+<h4>BackendArtifacts: Helper Interfaces</h4>
+<p>An Entry is a pivotal helper class and a Backend artifact. It represents a 
+  server side LDAP entry by containing attributes. It is the atomic unit of storage 
+  within a Backend. In this respect an Entry is a value object. Once instantiated 
+  by a Backend using the <code>newEntry</code> call, attribute value pair add 
+  methods are used to add single and multivalued attributes to new instances. 
+  Once all attributes are added the Entry can be stored in the Backend via <code>create</code>. 
+  Change and remove mutators are used afterwords on the validated Entry to alter 
+  its attributes. An Entry can be repeatedly updated this way until it is deleted. 
+  Backends and cursors provide the means to access and store entries while entries 
+  provide the means to alter attributes.</p>
+<p>Cursor artifacts extend the Backend interfaces to provide search and bulk access 
+  methods to efficiently iterate over entries. In very large DIBs entries cannot 
+  be loaded on mass due to resource limitations. Likewise search implementations 
+  must manage joins efficiently avoiding mass retreivals or table scans returning 
+  results on an as needed basis. Cursor are the iterative conduit into the DIB.</p>
+<p>The Cursor class is abstract and implemented as a NamingEnumeration that extends 
+  the Observable class. Cursors will interface with the Backend factory that created 
+  it. Most interfaces on Backends could potentially throw various subclasses of 
+  NamingException. It was natural to encapsulate all possible naming exceptions 
+  by implementing a NamingEnumeration. Observable was extended to potentially 
+  anounce open/close state changes to managing Backends so resouces associated 
+  with Cursors can be freed. The Backend interface does not extend Observable 
+  however. Not all backends will need to 'observe' these state changes. Implementations 
+  may decide to complement Cursor Observables by implementing the Observer interface, 
+  the potential is there if its use is needed. Also subtype interfaces of Backend, 
+  namely Atomic Backend and Unified Backend are designed to be used as service 
+  interfaces in the avalon framework. The design pattern could be compromised 
+  if the Observer's update method were to be exposed as a service interface.</p>
+<p>The abstract Cursor class declares the NamingException methods final and introduces 
+  the abstract protected advance, canAdvance, and freeResources methods requiring 
+  their implementation by concrete Cursors. These seals were introduced to allow 
+  the abstract Cursor class to automatically close itself without the chance of 
+  tampering by subclasses. When exhausted or when exceptions prevent further iteration, 
+  Cursors call close. Enumeration and NamingEnumeration semantics are maintained 
+  by wraping unexpected Backend exceptions in a subtype of NoSuchElement exception 
+  called CursorException. The JDK expects such an exception to be thrown when 
+  nextElement and next are called on exhausted instances of these interfaces.</p>
+<p>&nbsp;</p>
+<h3 align="center">Server Backend Semantics</h3>
+<p>Some operational semantics are supported by the syntax of classes and interfaces 
+  defined in the backend package. Others are not and need to be explicitly defined 
+  here. Exception handling semantics and iterative cursor behavoir are aspects 
+  of expected semantics in the server backend contract.</p>
+<p>&nbsp;</p>
+<h4>Exception Handling</h4>
+<p>To allow the gambit of Backend implementations, while balancing complexity 
+  with performance we quickly realized that most effective Backends must interface 
+  with a Schema subsystem. Attribute value and DN normalization will be a concern 
+  for every Backend. This is unavoidable. Attribute value normalization is specific 
+  to the type of the attribute and hence is dependent upon schema information.</p>
+<p>Value normalization requires valid values and introduces schema checking issues: 
+  object class correctness, attribute syntax correctness, attribute value syntaxes 
+  and matching rules. There simply is no way to avoid these aspects however they 
+  can and will be encapsulated within schema subsystem components provided to 
+  all backends as an independent service. Backends interface with the schema subsystem 
+  through Schemas and attribute Normalizer interfaces to conduct schema checking 
+  and validation. These interfaces will throw NamingException subclasses to announce 
+  schema violations. Backends need to remain as simple as possible. Minimizing 
+  the amount of exception handling within backends to manage calls to Schema and 
+  Normalizer interfaces keeps the complexity down. For these reasons many methods 
+  on backend package classes and interfaces throw subclasses of NamingExceptions. 
+  Hence Backends and their artifacts pass through exceptions generated by schema 
+  method calls. </p>
+<p>Backend implementations need not handle NamingExceptions generated by calls 
+  to dependent schema APIs. All Backends hence will need to have access to the 
+  schema subsystem. The backend package does not define how this actually happens 
+  nor does it place any restriction on the means. This is due to the use of various 
+  server composition schemes: embedded, standalone and micro-kernel configurations. 
+  Handling naming subsystem, schema subsystem and BackendException failures are 
+  the responsibility of protocol processing elements which sit above these modules 
+  in their various configurations.</p>
+<p>Exception bubble up from schema subsystem calls. The types of exceptions generated 
+  by the various interfaces hint at what they do. We have tried to distribute 
+  the number of exception types thrown by interfaces based on aspect. Some naming 
+  exceptions will result when validating the correctness of attribute values using 
+  attribute syntaxes and their identifiers. Others result in Entry level schema 
+  violations where the required attributes in an objectclass definition as missing 
+  or extra attributes not allowed by the schema have been added. We will coin 
+  the phrase entry level and attribute level to describe the two diffent types 
+  of violations we must manage. The Backend interface must manage entry level 
+  violations first because it is the facade through which all entries are created, 
+  updated and deleted. Secondly, restrictions on entry attribute composition cannot 
+  be managed by an Entry. Think about what would happend as an entry is being 
+  built. Stages in building it would of course be inconsistent with schema requirements 
+  on objectclasses. When an objectclass attribute is added the Entry is already 
+  inconsistent if that objectclass has manditory attribute requirements. What 
+  do you add first the objectclass attribute or the attributes it requires to 
+  be consistent with the objectclass definition? If the attributes required are 
+  added first they violate the schema by not having the appropriate objectclass 
+  attribute. To avoid this chicken or egg problem such Entry level schema checking 
+  is delegated to the Backend interfaces and avoided by Entry interfaces. Hence 
+  the contract presumes that Backend implementations manage objectclass schema 
+  checks while Entry implementations for that Backend manage attribute schema 
+  checks. </p>
+<p>Other non-schema subsystem associated exceptions (also NamingException subclasses) 
+  are thrown by the search method. These exceptions express problems with search 
+  criteria. Cursors created by searches can also throw other exceptions associated 
+  with resources or search limitations. These matters are left to the discretion 
+  of the backend implementors. Below we categorically list some of the exceptions 
+  that could be thrown by backend implementation classes which have conveniently 
+  been defined for us by the JNDI packages. Note that some exceptions in JNDI 
+  were defined specifically to express client side or client side provider errors. 
+  If we elect to use them we refer to them in our own serverside context.</p>
+<p><b>Cursor: </b></p>
+<p>Search errors (implicitly defined as NamingExceptions)</p>
+<ul>
+  <li>CannotProceedException</li>
+  <li>InterruptedNamingException</li>
+  <li>LimitExceededException</li>
+  <li>LinkException</li>
+  <li>LinkLoopException</li>
+  <li>MalformedLinkException</li>
+  <li>PartialResultException</li>
+  <li>SizeLimitExceededException</li>
+  <li>TimeLimitExceededException</li>
+</ul>
+<h4>Backend: </h4>
+<p> Objectclass level schema violations (explicitly thrown)</p>
+<ul>
+  <li>NameNotFoundException</li>
+  <li>InvalidNameException</li>
+  <li>NameAlreadyBoundException</li>
+  <li>ContextNotEmptyException</li>
+  <li>InvalidSearchFilterException</li>
+  <li>InvalidSearchControlsException</li>
+  <li>InvalidAttributesException</li>
+  <li>SchemaViolationException<br>
+  </li>
+</ul>
+<h4>Entry: </h4>
+Attribute level schema violations (explicitly thrown) 
+<ul>
+  <li>AttributeModificationException</li>
+  <li>InvalidAttributeValueException</li>
+  <li>InvalidAttributeIdentifierException</li>
+</ul>
+<p>&nbsp;</p>
+<h4>Cursor Behavoir</h4>
+<p>Cursors react to exceptional conditions based on the iteration methods used. 
+  If Cursors are used as Enumerations callers are sheilded from exception handling. 
+  Both backend and naming exceptions are logged and the Cursor is automatically 
+  closed if the hasNextElement method bombs. The nextElement method logs then 
+  transduces NamingExceptions and BackendExceptions into a CursorException which 
+  is a subclass of NoSuchElementException. Implementations must support the Enumeration 
+  contract by making sure that calls to nextElement preceded by hasNextElement 
+  call with a true return value never throw a CursorException. This can be accomplished 
+  by prefetching values.</p>
+<p>A similar contract is required for NamingEnumeration interfaces. The hasNext 
+  method sheilds callers from handling Backend exceptions while allowing them 
+  to handle NamingExceptions. BackendExceptions when encountered are logged and 
+  the Cursor is closed. Likewise the next method allows callers to handle NamingExceptions, 
+  yet sheilds them from BackendExceptions resulting from delegated calls to advance. 
+  It logs the BackendException and rethrows it as a CursorException. Again NamingEnumeration 
+  semantics must be adhered to. Calls to next should not throw a CursorException 
+  if hasNext returned true before the call. Prefetching again is the key.</p>
+<p>Concrete Cursor implementors may wonder how they can comply with the Enumeration 
+  and NamingEnumeration contract if all their interface methods are made final 
+  by the abstract Cursor class. Again prefetching is the key! Cursor implementors 
+  should prefetch at Cursor initialization. If the prefetch fails the BackendException 
+  can be handled by the caller to the Backend interface method instantiating the 
+  Cursor. If the prefetch yeilds no result a closed exhausted Cursor is returned. 
+  They are empty in such cases and the contract is satisfied.</p>
+<p>&nbsp;</p>
+</body>
+</html>

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/dnnormalization.html
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/dnnormalization.html	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,63 @@
+<html>
+<head>
+<title>LDAP &amp; X.500 Distinguished Name Normalization</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF" text="#000000">
+<p><font color="#999999">$Id: dnnormalization.html,v 1.1.1.1 2002/11/13 23:46:12 akarasulu Exp $</font></p>
+<h2 align="center">LDAP &amp; X.500 Distinguished Name Normalization</h2>
+
+<p>Distinguished names as specified by <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 
+  2253</a> or <a href="http://www.ietf.org/rfc/rfc1779.txt">RFC 1779</a> (obsoleted 
+  by 2253) should be normalized for Backend storage when used as keys. Without 
+  normalization equality matching against inconsistant user formated strings will 
+  fail miserably. Several articals and forums have debated how the normalization 
+  should proceed. In the 1.4 JDK a new X500Principal class handles normalization 
+  of distinguished names from a String representation and an BER encoder ASN.1 
+  representation. SUN describes it better than I can in the Javadocs for <a href="http://java.sun.com/j2se/1.4/docs/api/javax/security/auth/x500/X500Principal.html#getName(java.lang.String)">X500Principal</a>. 
+</p>
+<p>The Naming subsystem of our server shall use such a class or define it's own 
+  canonicalization of the distinguished name. The best bet is to use X500Principal 
+  yet there might be an advantage to making this aspect a pluggable feature since 
+  many ways exist and arguments over the matter are still unsettled.</p>
+<p>Regardless of the means used to generate DN keys (normalized DNs) the original 
+  user provided formating of the DN at creation or modification time shall be 
+  preserved under the Entry's attributes or its operational attributes depending 
+  on what DN we are refering to. So when we return the entry to the user it is 
+  formatted exactly in the way the user provided the DN in the first place. Some 
+  operational DN attributes managed by either the Backend or the naming subsystem 
+  of the server will be kept in normalized form.</p>
+<p>Backends receive entries for creation starting with the suffix or root Entry 
+  of their Directory Information Base (DIB). Operational keyed DNs must be provided 
+  in normalized and user provided formats. Other non-operational or administrative 
+  attributes may need normalization based on their specific attribute syntax if 
+  they are indexed: presuming the backend manages indices. The Backend must interface 
+  with both the schema subsystem and an attribute syntax specific normalizer to 
+  generate the appropriate normalized keys in canonical form for these attribute 
+  indices.</p>
+<p>Matching rules, attribute syntax, and attribute type information is required 
+  to correctly normalize attributes. For example case sensitive strings can not 
+  be case normalized (i.e. all lower cased) for use as keys. If case sensitive 
+  strings are case normalized we would not be able to store things like Unix file 
+  names: Notes.txt and notes.txt could not exist as separate files within a common 
+  directory. Because schema information is required to format/normalize/canonicalize 
+  attribute values for key representation the normalization functionality should 
+  be provided by either the Schema subsystem or by a Normalizer component that 
+  is dependent on the Schema subsystem or rather generated/provided by it. Figure 
+  1 shows how the intramodule dependencies would look:</p>
+<p><img src="NormalizerComponent.gif" width="601" height="168"></p>
+<p>The Backend subsystems would be dependent on components and interfaces within 
+  the schema subsystem. Normalizers will obviously be dependent on the nature 
+  of the attribute so the SchemaModule would provide access to one based on an 
+  attribute type via the Schema interface. A Backend would be provided a handle 
+  to the Schema subsystem by the server at some point in the module's lifecycle. 
+  The Backend in it's solid state would ask the SchemaModule for its Schema using 
+  its suffix as a parameter. The module would return the appropriate schema for 
+  that Backend. The Backend would then proceed to ask the Schema for an attribute 
+  specific Normalizer by providing the name of the attribute as a parameter to 
+  a getNormalizer method on the Schema interface. The Normalizer would then be 
+  used to generate a normalized attribute value from the user provided attribute 
+  value which can now be used by the Backend as a key.</p>
+</body>
+</html>

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/Database.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/Database.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,233 @@
+/*
+ * $Id: Database.java,v 1.4 2003/03/13 18:27:14 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm ;
+
+
+import javax.naming.Name ;
+import java.util.Iterator ;
+import java.math.BigInteger ;
+import javax.naming.NamingException ;
+
+import org.apache.eve.schema.Schema ;
+import org.apache.eve.backend.BackendException ;
+
+import org.apache.eve.backend.jdbm.index.Index ;
+import org.apache.eve.backend.jdbm.index.IndexCursor;
+import org.apache.eve.backend.Cursor ;
+
+import org.apache.regexp.RE ;
+import org.apache.commons.collections.MultiMap ;
+import org.apache.avalon.framework.logger.Logger;
+
+
+/**
+ *
+ */
+public interface Database
+{
+    public Schema getSchema() ;
+
+    //////////////////////
+    // Index Operations //
+    //////////////////////
+
+    public void addIndexOn(String an_attribute)
+        throws BackendException, NamingException ;
+
+    public boolean hasIndexOn(String an_attribute) ;
+
+    public Index getIndex(String an_attribute)
+        throws IndexNotFoundException ;
+
+    public BigInteger getEntryId(String a_dn)
+        throws BackendException, NamingException ;
+
+    public String getEntryDn(BigInteger a_id)
+        throws BackendException ;
+
+    public BigInteger getParentId(String a_dn)
+        throws BackendException, NamingException ;
+
+    public BigInteger getParentId(BigInteger a_childId)
+        throws BackendException ;
+
+    public int count()
+        throws BackendException ;
+
+    public int getIndexScanCount(String an_attribute)
+        throws BackendException, NamingException ;
+
+    public int getIndexScanCount(String an_attribute, String a_value)
+        throws BackendException, NamingException ;
+
+    public int getIndexScanCount(String an_attribute, String a_value,
+        boolean isGreaterThan)
+        throws BackendException, NamingException ;
+
+    public boolean assertIndexValue(String an_attribute, Object a_value,
+	    BigInteger a_id)
+        throws BackendException, NamingException ;
+
+    public boolean assertIndexValue(String an_attribute, Object a_value,
+	    BigInteger a_id, boolean isGreaterThan)
+        throws BackendException, NamingException ;
+
+    public IndexCursor getIndexCursor(String an_attribute)
+        throws BackendException, NamingException ;
+
+    public IndexCursor getIndexCursor(String an_attribute, String a_value)
+        throws BackendException, NamingException ;
+
+    public IndexCursor getIndexCursor(String an_attribute, String a_value,
+        boolean isGreaterThan)
+        throws BackendException, NamingException ;
+
+    public IndexCursor getIndexCursor(String an_attribute, RE a_regex)
+        throws BackendException, NamingException ;
+
+    public IndexCursor getIndexCursor(String an_attribute, RE a_regex,
+        String a_prefix)
+        throws BackendException, NamingException ;
+
+    //////////////////////////////////
+    // Master Table CRUD Operations //
+    //////////////////////////////////
+
+    public void create(LdapEntryImpl an_entry, BigInteger a_id)
+        throws BackendException, NamingException ;
+
+    public LdapEntryImpl read(BigInteger a_id)
+        throws BackendException, NamingException ;
+
+    public void update(LdapEntryImpl an_entry)
+        throws BackendException, NamingException ;
+
+    public void delete(BigInteger a_id)
+        throws BackendException, NamingException ;
+
+    /////////////////////////////
+    // Parent/Child Operations //
+    /////////////////////////////
+
+    public Cursor getChildren(BigInteger a_id)
+        throws BackendException, NamingException ;
+
+    public int getChildCount(BigInteger a_id)
+        throws BackendException, NamingException ;
+
+    public Name getSuffix() ;
+
+    public LdapEntryImpl getSuffixEntry()
+        throws BackendException, NamingException ;
+
+    public BigInteger getNextId()
+        throws BackendException ;
+
+    public BigInteger getCurrentId()
+        throws BackendException ;
+
+    public void sync() ;
+
+    public void close() ;
+
+    /////////////////////
+    // Utility Methods //
+    /////////////////////
+
+
+    public void setProperty(String a_propertyName, String a_propertyValue)
+        throws BackendException ;
+
+    public String getProperty(String a_propertyName)
+        throws BackendException ;
+
+    /**
+     * Lists only the User Defined Index (UDI) Attributes.
+     */
+    public Iterator getUDIAttributes() ;
+
+    public final static String [] SYS_INDICES =
+    { Schema.DN_ATTR, Schema.EXISTANCE_ATTR, Schema.HIERARCHY_ATTR} ;
+
+    /**
+     * Gets the names of the maditory system indices used by the database.
+     * These names are used as the [operational] 'attribute' or key names
+     * of the indices.  All cursor operations and assertion operations use
+     * these names to select the appropriate UDI (User Defined Index) or SDI
+     * (System Defined Index) to operate upon.
+     */
+	public String [] getSystemIndices() ;
+
+    public MultiMap getIndices(BigInteger a_id)
+        throws BackendException, NamingException ;
+
+    /**
+     * Modifies the relative distinguished name (RDN) of an entry
+     * without changing any parent child relationships.  This call
+     * has the side effect of altering the distinguished name of
+     * descendent entries if they exist.  The boolean argument will
+     * optionally remove the existing RDN attribute value pair
+     * replacing it with the new RDN attribute value pair.  If other
+     * RDN attribute value pairs exist besides the current RDN they
+     * will be spared.
+     *
+     * @param an_entry the entry whose RDN is to be modified.
+     * @param a_newRdn the new Rdn that is to replace the current Rdn.
+     * @param a_deleteOldRdn deletes the old Rdn attribute value pair if true.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    public void modifyRdn(org.apache.eve.backend.jdbm.LdapEntryImpl an_entry,
+        String a_newRdn, boolean a_deleteOldRdn)
+	    throws BackendException, NamingException ;
+
+    /**
+     * This overload combines the first two method operations into one.
+     * It changes the Rdn and the parent prefix at the same time while
+     * recursing name changes to all descendants of the child entry. It
+     * is obviously more complex than the other two operations alone and
+     * involves changes to both parent child indices and DN indices.
+     *
+     * @param a_parentEntry the parent the child is to subordinate to.
+     * @param a_childEntry the child to be moved under the parent.
+     * @param a_newRdn the new Rdn that is to replace the current Rdn.
+     * @param a_deleteOldRdn deletes the old Rdn attribute value pair if true.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    public void move(LdapEntryImpl a_parentEntry, LdapEntryImpl a_childEntry,
+	    String a_newRdn, boolean a_deleteOldRdn)
+	    throws BackendException, NamingException ;
+
+    /**
+     * Moves a child entry without changing the RDN under a new parent
+     * entry.  This effects the parent child relationship between the
+     * parent entry and the child entry.  The index for the child
+     * mapping it to the current parent is destroyed and a new index
+     * mapping it to the new parent is created.  As a side effect the
+     * name of the child entry and all its descendants will reflect the
+     * move within the DIT to a new parent.  The old parent prefix to
+     * the distinguished names of the child and its descendents will be
+     * replaced by the new parent DN prefix.
+     *
+     * @param a_parentEntry the parent the child is to subordinate to.
+     * @param a_childEntry the child to be moved under the parent.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    public void move(LdapEntryImpl a_parentEntry, LdapEntryImpl a_childEntry)
+	    throws BackendException, NamingException ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/EntryCursor.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/EntryCursor.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,66 @@
+/*
+ * $Id: EntryCursor.java,v 1.3 2003/03/13 18:27:14 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm ;
+
+
+import org.apache.eve.backend.Cursor ;
+import org.apache.eve.backend.BackendException ;
+import org.apache.eve.backend.jdbm.index.IndexRecord ;
+
+import javax.naming.NamingException ;
+
+/**
+ * @testcase org.apache.eve.backend.jdbm.TestEntryCursor 
+ */
+public class EntryCursor
+    extends Cursor
+{
+	protected final JdbmModule m_backend ;
+    protected final Cursor m_cursor ;
+
+    /**
+     * Creates a cursor over all the elements in the table.  The key will change
+     * to represent the value of key pointed to by this cursor.
+     *
+     * @param a_table Db to iterate over.
+     * @throws DbException if something goes drastically wrong.
+     */
+    public EntryCursor(JdbmModule a_backend, Cursor a_cursor)
+    {
+		m_cursor = a_cursor ;
+        m_backend = a_backend ;
+    }
+
+
+    protected Object advance()
+        throws BackendException, NamingException
+    {
+		IndexRecord l_rec = (IndexRecord) m_cursor.next() ;
+        return m_backend.read(l_rec.getEntryId()) ;
+    }
+
+
+    protected boolean canAdvance()
+        throws BackendException, NamingException
+    {
+		return m_cursor.hasMore() ;
+    }
+
+
+    protected void freeResources()
+    {
+        try {
+            m_cursor.close() ;
+        } catch (Exception e) {
+            e.printStackTrace() ;
+            getLogger().error("While closing underlying IndexCursor: ", e) ;
+        }
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/IndexNotFoundException.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/IndexNotFoundException.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,63 @@
+/*
+ * $Id: IndexNotFoundException.java,v 1.2 2003/03/13 18:27:15 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm ;
+
+
+import org.apache.eve.backend.BackendException ;
+
+
+public class IndexNotFoundException
+    extends BackendException
+{
+    public final String m_indexName ;
+
+    /**
+     * Constructs an Exception with a detailed message.
+     * @param a_message The message associated with the exception.
+     */
+    public IndexNotFoundException(String a_indexName)
+    {
+        super("Cannot efficiently search the DIB w/o an index on attribute "
+            + a_indexName + "\n. To allow such searches please contact the "
+            + "directory\nadministrator to create the index or to enable "
+            + "referals on searches using these\nattributes to a replica with "
+            + "the required set of indices.") ;
+        m_indexName = a_indexName ;
+    }
+
+
+    /**
+     * Constructs an Exception with a detailed message.
+     * @param a_message The message associated with the exception.
+     */
+    public IndexNotFoundException(String a_message, String a_indexName)
+    {
+        super(a_message) ;
+        m_indexName = a_indexName ;
+    }
+
+
+    /**
+     * Constructs an Exception with a detailed message and an error.
+     * @param a_message The message associated with the exception.
+     */
+    public IndexNotFoundException(String a_message, String a_indexName,
+        Throwable a_throwable)
+    {
+        super(a_message, a_throwable) ;
+        m_indexName = a_indexName ;
+    }
+
+
+    public String getIndexName()
+    {
+        return m_indexName ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/JdbmDatabase.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/JdbmDatabase.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,1049 @@
+/*
+ * $Id: JdbmDatabase.java,v 1.12 2003/08/06 03:01:25 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm ;
+
+
+import java.util.Map ;
+import java.util.HashMap ;
+import java.util.Iterator ;
+import java.util.Collection ;
+
+import javax.naming.Name ;
+import javax.naming.NameParser ;
+import javax.naming.NamingException ;
+
+import java.io.File ;
+import java.io.IOException ;
+
+import java.math.BigInteger ;
+import java.text.ParseException ;
+
+import org.apache.eve.backend.Cursor ;
+import org.apache.eve.schema.Schema ;
+import org.apache.eve.schema.Normalizer ;
+import org.apache.eve.backend.LdapEntry ;
+import org.apache.eve.backend.BackendException ;
+import org.apache.eve.backend.jdbm.index.Index ;
+import org.apache.eve.backend.jdbm.index.JdbmIndex ;
+import org.apache.eve.backend.jdbm.index.IndexRecord ;
+import org.apache.eve.backend.jdbm.table.MasterTable ;
+import org.apache.eve.backend.jdbm.index.IndexCursor ;
+
+import org.apache.regexp.RE ;
+import org.apache.avalon.framework.ExceptionUtil ;
+import org.apache.avalon.framework.logger.Logger ;
+import org.apache.avalon.framework.logger.AbstractLogEnabled ;
+
+import org.apache.commons.collections.MultiMap ;
+import org.apache.commons.collections.MultiHashMap ;
+
+import org.apache.ldap.common.name.DnParser ;
+import org.apache.ldap.common.util.NamespaceTools ;
+
+import jdbm.recman.RecordManager ;
+
+
+/**
+ * No caching just coordinated maintance of indices on CRUD operations for
+ * LdapEntrys in a BerkeleyDatabase.  This is not a module but a component
+ * used by the Berkeley Db Backend Module.
+ * @testcase <{TestDatabase}>
+ */
+public class JdbmDatabase
+    extends AbstractLogEnabled
+    implements Database
+{
+	public static boolean debug = true ;
+
+    public Logger log ;
+    private final RecordManager m_recMan ;
+    private final Name m_suffix ;
+    private final String m_wkdir ;
+    private final Schema m_schema ;
+    private final MasterTable m_master ;
+    private final JdbmIndex m_nameIdx ;
+    private final JdbmIndex m_existanceIdx ;
+    private final JdbmIndex m_hierarchyIdx ;
+    private final Map m_indices ;
+
+    private NameParser m_nonNormalizingParser = null ;
+    private final NameParser m_parser ;
+
+
+    public JdbmDatabase(final Schema a_schema,
+        final Name a_suffix,
+        final String a_wkdirPath)
+        throws BackendException, NamingException
+    {
+        m_schema = a_schema ;
+        m_suffix = a_suffix ;
+        m_wkdir = a_wkdirPath ;
+
+        m_parser = m_schema.getNormalizingParser() ;
+        try {
+            m_nonNormalizingParser = new DnParser() ;
+        } catch(IOException e) {
+            throw new BackendException("Could not initialize the non-" +
+                "normalizing DnParser", e) ;
+        }
+
+        try {
+            m_recMan = new RecordManager(a_wkdirPath
+                + File.separator + "master") ;
+            m_recMan.disableTransactions() ;
+        } catch(IOException e) {
+            String l_msg = "Could not initialize RecordManager:\n"
+                + ExceptionUtil.printStackTrace(e) ;
+            throw new BackendException(l_msg, e) ;
+        }
+
+        m_master = new MasterTable(m_recMan) ;
+        m_nameIdx = new JdbmIndex(Schema.DN_ATTR, m_schema, m_wkdir) ;
+        m_existanceIdx =
+            new JdbmIndex(Schema.EXISTANCE_ATTR, m_schema, m_wkdir) ;
+        m_hierarchyIdx =
+            new JdbmIndex(Schema.HIERARCHY_ATTR, m_schema, m_wkdir) ;
+        m_indices = new HashMap() ;
+    }
+
+
+    public Schema getSchema()
+    {
+        return m_schema ;
+    }
+
+
+    //////////////////////
+    // Index Operations //
+    //////////////////////
+
+
+    public void addIndexOn(String an_attribute)
+        throws BackendException, NamingException
+    {
+        an_attribute = an_attribute.toLowerCase() ;
+        if(m_schema.isBinary(an_attribute)) {
+            throw new BackendException(
+                "Indices not allowed on binary attributes!") ;
+        }
+
+        Normalizer l_normalizer = m_schema.getNormalizer(an_attribute, true) ;
+
+        if(null == l_normalizer) {
+            throw new BackendException("Cannot create index on attribute "
+                + an_attribute + " when schema does not have a normalizer for "
+                + "attribute " + an_attribute) ;
+            
+        }
+
+        an_attribute = an_attribute.toLowerCase() ;
+        JdbmIndex l_index = new JdbmIndex(an_attribute, m_schema, m_wkdir) ;
+        l_index.enableLogging(super.getLogger()) ;
+        m_indices.put(an_attribute, l_index) ;
+    }
+
+
+    public boolean hasIndexOn(String an_attribute)
+    {
+        try {
+            Index l_index = getIndex(an_attribute) ;
+            return l_index != null ;
+        } catch(IndexNotFoundException e) {
+            return false ;
+        }
+    }
+
+
+    public Index getIndex(String an_attribute)
+        throws IndexNotFoundException
+    {
+        String l_lowerCased = an_attribute.toLowerCase() ;
+
+        if(m_indices.containsKey(an_attribute)) {
+            return (Index) m_indices.get(an_attribute) ;
+        } else if(m_indices.containsKey(l_lowerCased)) {
+            return (Index) m_indices.get(l_lowerCased) ;
+        } else if(an_attribute.equals(Schema.DN_ATTR)) {
+            return m_nameIdx ;
+        } else if(an_attribute.equals(Schema.EXISTANCE_ATTR)) {
+            return m_existanceIdx ;
+        } else if(an_attribute.equals(Schema.HIERARCHY_ATTR)) {
+            return m_hierarchyIdx ;
+        } else {
+            throw new IndexNotFoundException("An index on attribute " +
+                an_attribute + " does not exist!") ;
+        }
+    }
+
+
+    public BigInteger getEntryId(String a_dn)
+        throws BackendException, NamingException
+    {
+        return m_nameIdx.getForward(a_dn) ;
+    }
+
+
+    public String getEntryDn(BigInteger a_id)
+        throws BackendException
+    {
+        return (String) m_nameIdx.getReverse(a_id) ;
+    }
+
+
+    public BigInteger getParentId(String a_dn)
+        throws BackendException, NamingException
+    {
+        BigInteger l_childId = m_nameIdx.getForward(a_dn) ;
+        return (BigInteger) m_hierarchyIdx.getReverse(l_childId) ;
+    }
+
+
+    public BigInteger getParentId(BigInteger a_childId)
+        throws BackendException
+    {
+        return (BigInteger) m_hierarchyIdx.getReverse(a_childId) ;
+    }
+
+
+    public int count()
+        throws BackendException
+    {
+        return m_master.count() ;
+    }
+
+
+    public int getIndexScanCount(String an_attribute)
+        throws BackendException, NamingException
+    {
+        return getIndex(an_attribute).count() ;
+    }
+
+
+    public int getIndexScanCount(String an_attribute, String a_value)
+        throws BackendException, NamingException
+    {
+        return getIndex(an_attribute).count(a_value) ;
+    }
+
+
+    public int getIndexScanCount(String an_attribute, String a_value,
+        boolean isGreaterThan)
+        throws BackendException, NamingException
+    {
+        return getIndex(an_attribute).count(a_value, isGreaterThan) ;
+    }
+
+
+    public boolean assertIndexValue(String an_attribute, Object a_value,
+	    BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        return getIndex(an_attribute).hasValue(a_value, a_id) ;
+    }
+
+
+    public boolean assertIndexValue(String an_attribute, Object a_value,
+	    BigInteger a_id, boolean isGreaterThan)
+        throws BackendException, NamingException
+    {
+        return getIndex(an_attribute).hasValue(a_value, a_id, isGreaterThan) ;
+    }
+
+
+    public IndexCursor getIndexCursor(String an_attribute)
+        throws BackendException, NamingException
+    {
+	    return getIndex(an_attribute).getCursor() ;
+    }
+
+
+    public IndexCursor getIndexCursor(String an_attribute, String a_value)
+        throws BackendException, NamingException
+    {
+	    return getIndex(an_attribute).getCursor(a_value) ;
+    }
+
+
+    public IndexCursor getIndexCursor(String an_attribute, String a_value,
+        boolean isGreaterThan)
+        throws BackendException, NamingException
+    {
+	    return getIndex(an_attribute).getCursor(a_value, isGreaterThan) ;
+    }
+
+
+    public IndexCursor getIndexCursor(String an_attribute, RE a_regex)
+        throws BackendException, NamingException
+    {
+	    return getIndex(an_attribute).getCursor(a_regex) ;
+    }
+
+
+    public IndexCursor getIndexCursor(String an_attribute, RE a_regex,
+        String a_prefix)
+        throws BackendException, NamingException
+    {
+	    return getIndex(an_attribute).getCursor(a_regex, a_prefix) ;
+    }
+
+
+    //////////////////////////////////
+    // Master Table CRUD Operations //
+    //////////////////////////////////
+
+
+    public void create(LdapEntryImpl an_entry, BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        String l_ldif = m_schema.getLdifComposer().compose(an_entry) ;
+        m_master.put(l_ldif, a_id) ;
+        addIndices(an_entry) ;
+        an_entry.validate() ;
+    }
+
+
+    public LdapEntryImpl read(BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        LdapEntryImpl l_entry = new LdapEntryImpl(this.m_schema) ;
+        l_entry.enableLogging(getLogger()) ;
+
+        String l_ldif = m_master.get(a_id) ;
+
+        if(getLogger().isDebugEnabled()) {
+            getLogger().debug("JdbmDatabase.read(BigInteger): Extracted the "
+                + "following LDIF from the master table:\n" + l_ldif) ;
+        }
+
+        try {
+            m_schema.getLdifParser().parse(l_entry, l_ldif) ;
+        } catch(ParseException e) {
+            throw new BackendException("Database may be corrupt.  Cannot "
+                + "parse ldif entries in backing store.", e) ;
+        }
+
+        if(getLogger().isDebugEnabled()) {
+            getLogger().debug("JdbmDatabase.read(BigInteger): Resusitated the "
+                + "following entry using the ldif parser:\n" + l_entry) ;
+        }
+
+        l_entry.validate() ;
+        return l_entry ;
+    }
+
+
+    public void update(LdapEntryImpl an_entry)
+        throws BackendException, NamingException
+    {
+        String l_ldif = m_schema.getLdifComposer().compose(an_entry) ;
+        m_master.put(l_ldif, an_entry.getEntryID()) ;
+        updateIndices(an_entry) ;
+        an_entry.validate() ;
+    }
+
+
+    public void delete(BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        if(getLogger().isDebugEnabled()) {
+            getLogger().debug("JdbmDatabase.delete(BigInteger): "
+                + "Deleting entry with id " + a_id) ;
+        }
+
+        LdapEntryImpl l_entry = read(a_id) ;
+
+        if(getLogger().isDebugEnabled()) {
+            getLogger().debug("JdbmDatabase.delete(BigInteger): "
+                + "Resusitated entry for deletion " + l_entry) ;
+        }
+
+        dropIndices(l_entry) ;
+        m_master.del(a_id) ;
+    }
+
+
+    /////////////////////////////
+    // Parent/Child Operations //
+    /////////////////////////////
+
+
+	public Cursor getChildren(BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        return m_hierarchyIdx.getCursor(a_id) ;
+    }
+
+
+	public int getChildCount(BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        return this.m_hierarchyIdx.count(a_id) ;
+    }
+
+
+	public Name getSuffix()
+    {
+        return m_suffix ;
+    }
+
+
+    public LdapEntryImpl getSuffixEntry()
+        throws BackendException, NamingException
+    {
+        return read(getEntryId(m_suffix.toString())) ;
+    }
+
+
+    public BigInteger getNextId()
+        throws BackendException
+    {
+        return m_master.getNextId() ;
+    }
+
+
+    public BigInteger getCurrentId()
+        throws BackendException
+    {
+        return m_master.getCurrentId() ;
+    }
+
+
+    public void sync()
+    {
+        Iterator l_list = m_indices.values().iterator() ;
+        while(l_list.hasNext()) {
+            Index l_index = (Index) l_list.next() ;
+
+            try {
+                l_index.sync() ;
+            } catch(Throwable t) {
+                super.getLogger().error("" //ProtocolModule.getMessageKey()
+                    + " - Database.sync(): Failed to sync index on attribute "
+                    + l_index.getAttribute()
+                    + " - index data may be lost.", t) ;
+            }
+        }
+
+        try {
+            this.m_existanceIdx.sync() ;
+        } catch(Throwable t) {
+	        getLogger().error("Failed to sync the existance table data may be "
+                + "lost.", t) ;
+        }
+
+        try {
+            this.m_hierarchyIdx.sync() ;
+        } catch(Throwable t) {
+	        getLogger().error("Failed to sync the hierarchy table data may be "
+                + "lost.", t) ;
+        }
+
+        try {
+            this.m_nameIdx.sync() ;
+        } catch(Throwable t) {
+	        getLogger().error("Failed to sync the distinguished name table "
+                + "data may be lost.", t) ;
+        }
+
+        try {
+            m_master.sync() ;
+        } catch(Throwable t) {
+	        getLogger().error("Failed to sync the master table data may be "
+                + "lost.", t) ;
+        }
+    }
+
+
+    public void close()
+    {
+        Iterator l_list = m_indices.values().iterator() ;
+        while(l_list.hasNext()) {
+            Index l_index = (Index) l_list.next() ;
+
+            try {
+               l_index.close() ;
+            } catch(Throwable t) {
+                getLogger().error("Failed to close index on attribute "
+                    + l_index.getAttribute()
+                    + " - index data may be lost.", t) ;
+            }
+        }
+
+        try {
+            this.m_existanceIdx.close() ;
+        } catch(Throwable t) {
+	        getLogger().error("Failed to close the existance table data may be "
+                + "lost.", t) ;
+        }
+
+        try {
+            this.m_hierarchyIdx.close() ;
+        } catch(Throwable t) {
+	        getLogger().error("Failed to close the hierarchy table data may be "
+                + "lost.", t) ;
+        }
+
+        try {
+            this.m_nameIdx.close() ;
+        } catch(Throwable t) {
+	        getLogger().error("Failed to close the distinguished name table "
+                + "data may be lost.", t) ;
+        }
+
+        try {
+            m_master.close() ;
+        } catch(Throwable t) {
+	        getLogger().error("Failed to close the master table data may be "
+                + "lost.", t) ;
+        }
+
+        try {
+            m_recMan.close() ;
+        } catch(Throwable t) {
+	        getLogger().error("Failed to close the database environment.", t) ;
+        }
+    }
+
+
+    public void enableLogging(Logger a_logger)
+    {
+        super.enableLogging(a_logger) ;
+        log = a_logger ;
+	    m_nameIdx.enableLogging(a_logger) ;
+        m_existanceIdx.enableLogging(a_logger) ;
+        m_hierarchyIdx.enableLogging(a_logger) ;
+        m_master.enableLogging(a_logger) ;
+    }
+
+
+    /////////////////////
+    // Utility Methods //
+    /////////////////////
+
+
+    public void setProperty(String a_propertyName, String a_propertyValue)
+        throws BackendException
+    {
+        m_master.setProperty(a_propertyName, a_propertyValue) ;
+    }
+
+
+    public String getProperty(String a_propertyName)
+        throws BackendException
+    {
+        return this.m_master.getProperty(a_propertyName) ;
+    }
+
+
+    /**
+     * Lists only the User Defined Index (UDI) Attributes.
+     */
+    public Iterator getUDIAttributes()
+    {
+        return m_indices.keySet().iterator() ;
+    }
+
+
+    public final static String [] SYS_INDICES =
+    { Schema.DN_ATTR, Schema.EXISTANCE_ATTR, Schema.HIERARCHY_ATTR} ;
+
+    /**
+     * Gets the names of the maditory system indices used by the database.
+     * These names are used as the [operational] 'attribute' or key names
+     * of the indices.  All cursor operations and assertion operations use
+     * these names to select the appropriate UDI (User Defined Index) or SDI
+     * (System Defined Index) to operate upon.
+     */
+	public String [] getSystemIndices()
+    {
+		return SYS_INDICES ;
+    }
+
+
+    public MultiMap getIndices(BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        MultiHashMap l_map = new MultiHashMap() ;
+
+        // Get the distinguishedName to id mapping
+        l_map.put("distinguishedName", m_nameIdx.getReverse(a_id)) ;
+
+        // Get all standard index attribute to value mappings
+        Iterator l_indices = m_indices.values().iterator() ;
+        while(l_indices.hasNext()) {
+            Index l_index = (Index) l_indices.next() ;
+            Cursor l_cursor = l_index.getReverseCursor(a_id) ;
+            while(l_cursor.hasMore()) {
+                IndexRecord l_rec = (IndexRecord) l_cursor.next() ;
+                Object l_val = l_rec.getIndexKey() ;
+                l_map.put(l_index.getAttribute(), l_val) ;
+            }
+        }
+
+        // Get all existance mappings for this id creating a special key
+        // that looks like so 'existance[attribute]' and the value is set to id
+        Cursor l_cursor = m_existanceIdx.getReverseCursor(a_id) ;
+        StringBuffer l_val = new StringBuffer() ;
+        while(l_cursor.hasMore()) {
+            IndexRecord l_rec = (IndexRecord) l_cursor.next() ;
+            l_val.append("existance[").append(l_rec.getIndexKey()).append(']') ;
+            l_map.put(l_val.toString(), l_rec.getEntryId()) ;
+            l_val.setLength(0) ;
+        }
+
+        // Get all parent child mappings for this entry as the parent using the
+        // key 'child' with many entries following it.
+        l_cursor = this.m_hierarchyIdx.getCursor(a_id) ;
+        while(l_cursor.hasMore()) {
+            IndexRecord l_rec = (IndexRecord) l_cursor.next() ;
+            l_map.put("child", l_rec.getEntryId()) ;
+        }
+
+        return l_map ;
+    }
+
+
+    private void addIndices(LdapEntry an_entry)
+        throws BackendException, NamingException
+    {
+        BigInteger l_id = ((LdapEntryImpl) an_entry).getEntryID() ;
+        Iterator l_list = an_entry.attributes().iterator() ;
+
+        m_nameIdx.add(an_entry.getNormalizedDN().toString(), l_id) ;
+
+        if(getLogger().isDebugEnabled() && debug) {
+			getLogger().debug("Adding name index ("
+                + an_entry.getNormalizedDN().toString()
+                + ", " + l_id.toString() + ")") ;
+        }
+
+        m_hierarchyIdx.add(((LdapEntryImpl) an_entry).getParentID(), l_id) ;
+
+        while(l_list.hasNext()) {
+            String l_attribute = (String) l_list.next() ;
+            String l_tolower = l_attribute.toLowerCase() ;
+
+            if(m_indices.containsKey(l_tolower)) {
+	            Index l_index = (Index) m_indices.get(l_tolower) ;
+	            Iterator l_values = // Get values using correct case.
+                    an_entry.getMultiValue(l_attribute).iterator() ;
+                while(l_values.hasNext()) {
+                    l_index.add((String) l_values.next(), l_id) ;
+                }
+
+                // Existance index will never be hit since 'existance' is not an
+	            // attribute based index in the formal sense. Nor are the name
+                // and hierarchy indices.
+	            m_existanceIdx.add(l_tolower, l_id) ;
+            }
+        }
+    }
+
+
+    private void dropIndices(LdapEntry an_entry)
+        throws BackendException, NamingException
+    {
+        if(getLogger().isDebugEnabled()) {
+            getLogger().debug("JdbmModule.dropIndices(): deleting "
+                + "indices on entry\n" + an_entry) ;
+        }
+
+        BigInteger l_id = ((LdapEntryImpl) an_entry).getEntryID() ;
+        Iterator l_list = an_entry.attributes().iterator() ;
+
+        m_nameIdx.drop(an_entry.getNormalizedDN().toString(), l_id) ;
+        m_hierarchyIdx.drop(
+            ((LdapEntryImpl) an_entry).getParentID(), l_id) ;
+
+        while(l_list.hasNext()) {
+            String l_attribute = ((String) l_list.next()) ;
+            String l_tolower = l_attribute.toLowerCase() ;
+
+            if(m_indices.containsKey(l_tolower)) {
+	            Index l_index = (Index) m_indices.get(l_tolower) ;
+	            Iterator l_values = // Get values using correct case.
+                    an_entry.getMultiValue(l_attribute).iterator() ;
+                while(l_values.hasNext()) {
+                    l_index.drop((String) l_values.next(), l_id) ;
+                }
+
+                // Existance index will never be hit since 'existance' is not an
+	            // attribute based index in the formal sense. Nor are the name
+                // and hierarchy indices.
+	            m_existanceIdx.drop(l_tolower, l_id) ;
+            }
+        }
+    }
+
+
+    private void updateIndices( LdapEntryImpl an_entry )
+        throws BackendException, NamingException
+    {
+        if ( getLogger().isDebugEnabled() ) 
+        {
+            getLogger().debug( "" //ProtocolModule.getMessageKey()
+                + " - Database.updateIndices() - dumping delta hashes "
+                + "after removing the old dn and adding the new Dn" ) ;
+            getLogger().debug( "Added hash:\n" + an_entry.getAdded() ) ;
+            getLogger().debug( "Removed hash:\n" + an_entry.getRemoved() ) ;
+        }
+
+	    BigInteger l_id = an_entry.getEntryID() ;
+
+        //
+        // Remove indices for all attributes removed.
+        //
+
+        Iterator l_removedAttribs = an_entry.getRemoved().keySet().iterator() ;
+	    
+        while ( l_removedAttribs.hasNext() ) 
+        {
+	        String l_removedAttrib = ( String ) l_removedAttribs.next() ;
+	        String l_tolower = l_removedAttrib.toLowerCase() ;
+
+            if ( m_indices.containsKey( l_tolower ) ) 
+            {
+                Index l_index = ( Index ) m_indices.get( l_tolower ) ;
+
+                // Remove indices for all the removed values.
+	            Iterator l_values = ( ( Collection )
+	                an_entry.getRemoved().get( l_removedAttrib ) ).iterator() ;
+	            while ( l_values.hasNext() ) 
+                {
+                    Object l_val = l_values.next() ;
+                    if ( getLogger().isDebugEnabled() ) 
+                    {
+                        getLogger().debug( "" //ProtocolModule.getMessageKey()
+                            + " - Database.updateIndices() - removing index "
+                            + l_tolower + " with value " + l_val ) ;
+                    }
+	                l_index.drop( l_val, l_id ) ;
+	            }
+
+                // Existance table must be handled specifically since it is not
+	            // an [operational] attribute and is not caught by the loop
+                // above. We need to make sure that we only remove an existance
+	            // index if all of the values for that attribute have been
+                // dropped.  We also need to watch out for stale keys to empty
+	            // collections in the multimap.
+	            if ( ! an_entry.containsKey( l_removedAttrib ) ) 
+                {
+	                m_existanceIdx.drop( l_tolower, l_id ) ;
+	            }
+	            else if ( an_entry.containsKey( l_removedAttrib ) &&
+	               an_entry.getMultiValue( l_removedAttrib ).size() == 0 )
+	            {
+	                m_existanceIdx.drop( l_tolower, l_id ) ;
+	            }
+            }
+            else if ( l_tolower.equals( "parentid" ) )
+            {
+                Iterator l_list = ( ( Collection ) 
+                    an_entry.getRemoved().get( l_removedAttrib) ).iterator() ;
+                    
+                while( l_list.hasNext() )
+                {
+                    BigInteger l_oldParentId = null ;
+                    Object l_obj = l_list.next() ;
+                    
+                    if ( l_obj instanceof String )
+                    {
+                        l_oldParentId = new BigInteger( ( String ) l_obj ) ; 
+                    }
+                    else if ( l_obj instanceof BigInteger )
+                    {
+                        l_oldParentId = ( BigInteger ) l_obj ;
+                    }
+                    
+                        
+                    m_hierarchyIdx.drop( l_oldParentId, l_id ) ;
+                    
+                    if( getLogger().isDebugEnabled() )
+                    {
+                        getLogger().debug( "removed old parentid '" 
+                            + l_oldParentId + "' from hierarchy index" ) ;
+                    }
+                }
+            } // Now we need to check for DN attribute changes due to modify ops
+            else if( l_tolower.equals( Schema.DN_ATTR ) ) 
+            {
+                Iterator l_list = ( ( Collection )
+                    an_entry.getRemoved().get( l_removedAttrib ) ).iterator() ;
+
+                while( l_list.hasNext() ) 
+                {
+                    Object l_oldDn = l_list.next() ;
+                    m_nameIdx.drop( l_oldDn, l_id ) ;
+
+                    if( getLogger().isDebugEnabled() ) 
+                    {
+                        getLogger().debug( "removed old Dn '" + l_oldDn 
+                            + "' from name index" ) ;
+                    }
+                }
+            } // Close else if
+        } // Close while
+
+
+        //
+        // Create indices for all attribute value pairs added.
+        //
+
+        Iterator l_addedAttribs = an_entry.getAdded().keySet().iterator() ;
+	    while ( l_addedAttribs.hasNext() ) 
+        {
+	        String l_addedAttrib = ( String ) l_addedAttribs.next() ;
+            String l_tolower = l_addedAttrib.toLowerCase() ;
+
+            if ( m_indices.containsKey( l_tolower ) ) 
+            {
+                Index l_index = ( Index ) m_indices.get( l_tolower ) ;
+
+                // Create indices for all the added values.
+	            Iterator l_values = ( ( Collection )
+	                an_entry.getAdded().get( l_addedAttrib ) ).iterator() ;
+	            while ( l_values.hasNext() )  
+                {
+                    Object l_val = l_values.next() ;
+                    l_index.add( l_val, l_id ) ;
+                    
+                    if( getLogger().isDebugEnabled() ) 
+                    {
+                        getLogger().debug( "" //ProtocolModule.getMessageKey()
+                            + " - Database.updateIndices() - added index "
+                            + l_tolower + " with value " + l_val ) ;
+                    }
+	            }
+
+                // Add an index into the existance table
+	            m_existanceIdx.add( l_tolower, l_id ) ;
+            } // Now we need to check for DN attribute changes due to modify ops
+            else if ( l_tolower.equals( "parentid" ) )
+            {
+                Iterator l_list = ( ( Collection )
+                    an_entry.getAdded().get( l_addedAttrib ) ).iterator() ;
+
+                while ( l_list.hasNext() ) 
+                {
+                    BigInteger l_newParentId = null ;
+                    Object l_obj = l_list.next() ;
+                    
+                    if ( l_obj instanceof String )
+                    {
+                        l_newParentId = new BigInteger( ( String ) l_obj ) ; 
+                    }
+                    else if ( l_obj instanceof BigInteger )
+                    {
+                        l_newParentId = ( BigInteger ) l_obj ;
+                    }
+                    
+                    m_hierarchyIdx.add( l_newParentId, l_id ) ;
+
+                    if ( getLogger().isDebugEnabled() ) 
+                    {
+                        getLogger().debug( "added new Dn '" + l_newParentId 
+                            + "' to hierarchy index" ) ;
+                    }
+                }
+            }
+            else if ( l_tolower.equals( Schema.DN_ATTR ) ) 
+            {
+                Iterator l_list = ( ( Collection )
+                    an_entry.getAdded().get( l_addedAttrib ) ).iterator() ;
+
+                while ( l_list.hasNext() ) 
+                {
+                    String l_newDn = ( String ) l_list.next() ;
+                    m_nameIdx.add( l_newDn, l_id ) ;
+
+                    if ( getLogger().isDebugEnabled() ) 
+                    {
+                        getLogger().debug( "added new Dn '"
+                            + l_newDn + "' to name index" ) ;
+                    }
+                }
+            } // Close else if
+        } // Close while
+    } // Close updateIndices()
+
+
+    /**
+     * Modifies the relative distinguished name (RDN) of an entry
+     * without changing any parent child relationships.  This call
+     * has the side effect of altering the distinguished name of
+     * descendent entries if they exist.  The boolean argument will
+     * optionally remove the existing RDN attribute value pair
+     * replacing it with the new RDN attribute value pair.  If other
+     * RDN attribute value pairs exist besides the current RDN they
+     * will be spared.
+     *
+     * @param an_entry the entry whose RDN is to be modified.
+     * @param a_newRdn the new Rdn that is to replace the current Rdn.
+     * @param a_deleteOldRdn deletes the old Rdn attribute value pair if true.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    public void modifyRdn( LdapEntryImpl an_entry,
+        String a_newRdn, boolean a_deleteOldRdn )
+	    throws BackendException, NamingException
+    {
+        String l_attribute = NamespaceTools.getRdnAttribute( a_newRdn ) ;
+	    String l_value = NamespaceTools.getRdnValue( a_newRdn ) ;
+
+        // This name is not Normalized since we want to preserve the user
+        // provided version of the Dn.
+	    Name l_name = m_nonNormalizingParser.parse( an_entry.getEntryDN() ) ;
+
+        if ( a_deleteOldRdn ||
+            m_schema.isSingleValue( l_attribute.toLowerCase() ) )
+        {
+	        String l_oldRdn = l_name.get( l_name.size() - 1 ) ;
+            String l_oldAttribute = NamespaceTools.getRdnAttribute( l_oldRdn ) ;
+            String l_oldValue = NamespaceTools.getRdnValue( l_oldRdn ) ;
+            
+	        an_entry.removeValue( l_oldAttribute, l_oldValue ) ;
+	    }
+
+        l_name.remove( l_name.size() - 1 ) ;
+	    l_name.add( l_name.size(), a_newRdn ) ;
+	    an_entry.addValue( l_attribute, l_value ) ;
+
+        if( getLogger().isDebugEnabled() ) 
+        {
+            getLogger().debug( "" //ProtocolModule.getMessageKey()
+                + " - Database.modifyRdn() - modifyDn() on entry '" 
+                + an_entry.getEntryDN() + "' using new Dn of '" 
+                + l_name + "'" ) ;
+        }
+
+	    modifyDn( ( LdapEntryImpl ) an_entry, l_name) ;
+    }
+
+
+    /**
+     * Recursively modifies the distinguished name of an entry and the names of
+     * its descendants calling itself in the recursion.
+     *
+     * @param an_entry Entry being altered to have a new DN.
+     * @param a_dn Distinguished name to set as the new DN
+     */
+    void modifyDn(LdapEntryImpl an_entry, Name a_dn)
+	    throws BackendException, NamingException
+    {
+        LdapEntryImpl l_child ;
+	    Cursor l_children ;
+	    Name l_childDN ;
+
+        if ( getLogger().isDebugEnabled() ) 
+        {
+            getLogger().debug( "" //ProtocolModule.getMessageKey()
+                + " - Database.modifyDn() - called on entry '"
+                + an_entry.getNormalizedDN() + "' to change it's DN to '"
+                + a_dn ) ;
+        }
+	
+        // Package friendly method that sets the user provided dn using
+	    // a name.  It automatically updates the normalized dn so it is
+	    // synchronized with the change.  setEntryDN must add the 
+	    // appropriate hints in the entry so the update can properly modify
+	    // indices.  If it seems like too much of a hassle then we can
+	    // handle index maintenance here with replication of update function
+	    // -ality.
+	    an_entry.setEntryDN( a_dn ) ;
+	    update( an_entry ) ;
+	    l_children = getChildren( an_entry.getEntryID() ) ;
+	
+        // List children using the DN since the updated parent has had
+	    // it's indices updated listChildren will lookup the id by dn
+	    // then use the parent child index to lookup all the children.
+	    while( l_children.hasMore() ) 
+        {
+            IndexRecord l_rec = ( IndexRecord ) l_children.next() ;
+	        l_child = read( l_rec.getEntryId() ) ;
+	        l_childDN = ( Name ) a_dn.clone() ;
+
+            // Add to the copy of the parent the unnormalized rdn of the child
+            // Dn to get the new Dn of the child entry - call recursively.
+	        l_childDN.add( 0,
+                NamespaceTools.getRdn( l_child.getEntryDN() ) ) ;
+            modifyDn( l_child, l_childDN ) ;
+	    }
+    }
+
+
+    /**
+     * This overload combines the first two method operations into one.
+     * It changes the Rdn and the parent prefix at the same time while
+     * recursing name changes to all descendants of the child entry. It
+     * is obviously more complex than the other two operations alone and
+     * involves changes to both parent child indices and DN indices.
+     *
+     * @param a_parentEntry the parent the child is to subordinate to.
+     * @param a_childEntry the child to be moved under the parent.
+     * @param a_newRdn the new Rdn that is to replace the current Rdn.
+     * @param a_deleteOldRdn deletes the old Rdn attribute value pair if true.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    public void move(LdapEntryImpl a_parentEntry, LdapEntryImpl a_childEntry,
+	    String a_newRdn, boolean a_deleteOldRdn)
+	    throws BackendException, NamingException
+    {
+        // @TODO NOT A VERY EFFICENT OPERATION AT ALL ! ! !  FIX IT ! ! !
+	    ( ( LdapEntryImpl ) a_childEntry ).setParent( ( LdapEntryImpl ) 
+            a_parentEntry ) ;
+        modifyRdn( a_childEntry, a_newRdn, a_deleteOldRdn ) ;
+        move( a_parentEntry, a_childEntry ) ;
+    }
+
+
+    /**
+     * Moves a child entry without changing the RDN under a new parent
+     * entry.  This effects the parent child relationship between the
+     * parent entry and the child entry.  The index for the child
+     * mapping it to the current parent is destroyed and a new index
+     * mapping it to the new parent is created.  As a side effect the
+     * name of the child entry and all its descendants will reflect the
+     * move within the DIT to a new parent.  The old parent prefix to
+     * the distinguished names of the child and its descendents will be
+     * replaced by the new parent DN prefix.
+     *
+     * @param a_parentEntry the parent the child is to subordinate to.
+     * @param a_childEntry the child to be moved under the parent.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    public void move( LdapEntryImpl a_parentEntry, LdapEntryImpl a_childEntry )
+	    throws BackendException, NamingException
+    {
+        Name l_parentDn =
+            m_nonNormalizingParser.parse( a_parentEntry.getEntryDN() ) ;
+	    Name l_childDn =
+            m_nonNormalizingParser.parse( a_childEntry.getEntryDN() ) ;
+	    String l_rdn = l_childDn.get( l_childDn.size() - 1 ) ;
+	
+        // This call should be package friendly and should automatically
+	    // replace the current parent id and dn operational attributes 
+	    // with the new parent's respective operational attribute values.
+	    ( ( LdapEntryImpl ) a_childEntry ).setParent( 
+            ( LdapEntryImpl ) a_parentEntry ) ;
+            
+	    Name l_newDn = l_parentDn ;
+	    l_newDn.add( l_newDn.size(), l_rdn ) ;
+	    modifyDn( ( LdapEntryImpl ) a_childEntry, l_newDn ) ;
+    }
+}
+

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/JdbmModule.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/JdbmModule.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,596 @@
+/*
+ * $Id: JdbmModule.java,v 1.11 2003/08/06 03:01:25 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm ;
+
+
+import java.math.BigInteger ;
+
+import javax.naming.Name ;
+import javax.naming.NamingException ;
+import javax.naming.NameNotFoundException ;
+import javax.naming.ContextNotEmptyException ;
+import javax.naming.NameAlreadyBoundException ;
+
+import org.apache.eve.backend.Cursor ;
+import org.apache.ldap.common.filter.ExprNode ;
+import org.apache.eve.backend.LdapEntry ;
+import org.apache.eve.schema.SchemaManager ;
+import org.apache.eve.backend.BackendModule ;
+import org.apache.eve.backend.BackendException ;
+import org.apache.eve.backend.jdbm.search.SearchEngine ;
+
+import org.apache.avalon.framework.ExceptionUtil ;
+import org.apache.avalon.framework.service.ServiceManager ;
+import org.apache.avalon.framework.service.ServiceException ;
+import org.apache.avalon.framework.configuration.Configuration ;
+import org.apache.avalon.framework.configuration.ConfigurationException ;
+
+import org.apache.commons.collections.LRUMap ;
+import org.apache.eve.backend.jdbm.gui.BackendFrame ;
+
+
+/**
+ * Jdbm backend module.
+ * 
+ * @phoenix:block
+ * @phoenix:service name="org.apache.eve.backend.AtomicBackend"
+ * @phoenix:mx-topic name="jdbm-backend"
+ */
+public class JdbmModule
+    extends BackendModule
+{
+    private JdbmDatabase m_db = null ;
+    private SearchEngine m_searchEngine = null ;
+
+    private Object m_cacheLock = new Object() ;
+    private LRUMap m_byIdCache = new LRUMap() ;
+
+
+    public JdbmDatabase getDatabase()
+    {
+        return m_db ;
+    }
+
+
+    private void addToCache(org.apache.eve.backend.jdbm.LdapEntryImpl an_entry)
+        throws NamingException
+    {
+        synchronized(m_cacheLock) {
+            super.m_cache.put(an_entry.getNormalizedDN().toString(), an_entry) ;
+            this.m_byIdCache.put(an_entry.getEntryID(), an_entry) ;
+        }
+    }
+
+
+    private void removeFromCache(org.apache.eve.backend.jdbm.LdapEntryImpl an_entry)
+        throws NamingException
+    {
+        synchronized(m_cacheLock) {
+            super.m_cache.remove(an_entry.getNormalizedDN().toString()) ;
+            this.m_byIdCache.remove(an_entry.getEntryID()) ;
+        }
+    }
+
+
+    //////////////////////////////////
+    // Backend Crud Implementations //
+    //////////////////////////////////
+
+
+    public void delete( LdapEntry an_entry )
+        throws BackendException, NamingException
+    {
+        LdapEntryImpl l_entry = ( LdapEntryImpl ) an_entry ;
+
+        if( ! l_entry.isValid() )
+        {
+            throw new BackendException( "Cannot perform delete operation on "
+                + "invalid entry: " + l_entry ) ;
+        }
+
+        if( m_db.getChildCount( l_entry.getEntryID() ) > 0 )
+        {
+            // Exception translates to NOTALLOWEDONLEAF LDAPv3 result code (66)
+            ContextNotEmptyException e = new ContextNotEmptyException(
+                "[66] Cannot delete entry " + l_entry.getEntryDN()
+                + " it has children!" ) ;
+            e.setRemainingName( l_entry.getNormalizedDN() ) ;
+            throw e ;
+        }
+
+        BigInteger l_id = l_entry.getEntryID() ;
+        m_db.delete( l_id ) ;
+        removeFromCache( ( LdapEntryImpl ) an_entry ) ;
+    }
+
+
+    /**
+     * Creates a new invalid entry ready to be populated and created within this
+     * backend module.
+     *
+     * @param a_dn the non-normalized user provided distinguished name of the
+     * new entry to create.
+     * @throws NameAlreadyBoundException if an entry with a_dn already exists
+     * within this backend.
+     * @throws javax.naming.InvalidNameException if a_dn does not conform to the dn syntax
+     * @throws NameNotFoundException if a_dn is not suffix and does not have a
+     * parent to be attached to.
+     */
+    public LdapEntry newEntry( String a_dn )
+        throws BackendException, NamingException
+    {
+        // Will throw InvalidNameException if a_dn is syntactically incorrect.
+        Name l_dn = m_schema.getNormalizingParser().parse( a_dn ) ;
+        if( m_db.getEntryId( l_dn.toString() ) != null )
+        {
+            // NameAlreadyBoundExceptions correspond to a NAMEALREADYEXISTS
+            // result code with a value of [68] within the LDAPv3 protocol.
+	        NameAlreadyBoundException e = new NameAlreadyBoundException(
+                "[68] '" + a_dn + "' is already bound." ) ;
+            e.setResolvedName( l_dn ) ;
+            throw e ;
+        }
+
+        //
+        // We only check for the existance of a parent entry if the new entry
+        // to add is not a suffix.  This is because a suffix entry will not
+        // have a parent node.
+        //
+
+        if( ! l_dn.equals( getSuffix() ) )
+        {
+            Name l_parent = l_dn.getSuffix( 1 ) ;
+            if( m_db.getEntryId( l_parent.toString() ) == null )
+            {
+                // NameNotFoundExceptions in JNDI correspond to the NOSUCHOBJECT
+                // result code of the LdapResult of the response in LDAPv3
+                NameNotFoundException e = new NameNotFoundException(
+                    "[32] Parent entry '" + l_parent
+					+ "' does not exist.  Cannot create " + l_dn.get(0)
+                    + " without parent." ) ;
+                throw e ;
+            }
+        }
+
+        LdapEntryImpl l_entry = new LdapEntryImpl( m_schema, l_dn, a_dn ) ;
+        l_entry.enableLogging( getLogger() ) ;
+        return l_entry ;
+    }
+
+
+    public LdapEntry read( Name a_dn )
+        throws BackendException, NamingException
+    {
+        if( m_cache.containsKey( a_dn.toString() ) )
+        {
+            return ( LdapEntry ) m_cache.get( a_dn.toString() ) ;
+        }
+
+        BigInteger l_id = m_db.getEntryId( a_dn.toString() ) ;
+
+        if( l_id == null )
+        {
+	        throw new NameNotFoundException( "[32] '" + a_dn
+                + "' does not exist!" ) ;
+        }
+
+        LdapEntryImpl l_entry = m_db.read( l_id ) ;
+        l_entry.enableLogging( getLogger() ) ;
+        addToCache( l_entry ) ;
+        return l_entry ;
+    }
+
+
+    LdapEntry read(BigInteger l_id)
+        throws BackendException, NamingException
+    {
+        if(this.m_byIdCache.containsKey(l_id)) {
+            return (LdapEntry) m_byIdCache.get(l_id) ;
+        }
+
+        LdapEntryImpl l_entry = m_db.read(l_id) ;
+        l_entry.enableLogging(getLogger()) ;
+        addToCache(l_entry) ;
+        return l_entry ;
+    }
+
+
+    /**
+     * Updates an entry in the backing store.
+     *
+     * @param an_entry the modified entry to update.
+     */
+    public void update(LdapEntry an_entry)
+        throws BackendException, NamingException
+    {
+        removeFromCache((LdapEntryImpl) an_entry) ;
+	    m_db.update((LdapEntryImpl) an_entry) ;
+        addToCache((LdapEntryImpl) an_entry) ;
+    }
+
+
+    public void create(LdapEntry an_entry)
+        throws BackendException, NamingException
+    {
+        LdapEntryImpl l_entry = (LdapEntryImpl) an_entry ;
+
+        if(l_entry.isValid()) {
+            throw new BackendException("Cannot create entries that have "
+                + "already been created!") ;
+        }
+
+        // Add operational attributes!
+        BigInteger l_id = m_db.getNextId() ;
+        LdapEntryImpl l_parent = null ;
+        BigInteger l_parentId = null ;
+        l_entry.put(LdapEntry.ID_ATTR, l_id.toString()) ;
+
+        if(l_entry.getNormalizedDN().equals(m_suffix)) {
+            l_entry.put(LdapEntry.PARENTID_ATTR, l_id.toString()) ;
+            l_entry.put(LdapEntry.PARENTDN_ATTR, l_entry.getEntryDN()) ;
+            l_parent = l_entry ;
+            l_parentId = l_id ;
+        } else {
+            l_parent = (LdapEntryImpl) getParent(l_entry.getNormalizedDN()) ;
+            l_parentId = l_parent.getEntryID() ;
+            l_entry.put(LdapEntry.PARENTID_ATTR, l_parentId.toString()) ;
+            l_entry.put(LdapEntry.PARENTDN_ATTR, l_parent.getEntryDN()) ;
+        }
+
+        m_db.create(l_entry, l_id) ;
+        addToCache(l_entry) ;
+    }
+
+
+    ///////////////////////////////////////
+    // Search and Modify Implementations //
+    ///////////////////////////////////////
+
+
+    /**
+     * Searches for candidate entries on the backend starting on a base DN 
+     * using a search filter with search controls.
+     * 
+     * The distinguished name argument is NOT presumed to be normalized in
+     * accordance with schema attribute syntax and attribute matching rules.
+     * The DN is also NOT presumed to be syntacticly correct or within the
+     * namespace of this directory information base.  Unaware of normalization
+     * this method will attempt to normalize any DN arguements.  Apriori
+     * normalization would be redundant.
+     *
+     * W O R K   I N   P R O G R E S S
+     *
+     * @param a_filter String representation of an LDAP search filter.
+     * @param a_baseDn String representing the base of the search.
+     * @param a_scope SearchControls governing how this search is to be
+     * conducted.
+     * @throws BackendException on Backend errors or when the operation cannot
+     * proceed due to a malformed search filter, a non-existant search base, or
+     * inconsistant search controls.
+     * @throws javax.naming.InvalidNameException if a_baseDN is not syntactically correct.
+     * @throws NameNotFoundException when a component of a_baseDN cannot be
+     * resolved because it is not bound.
+     * @throws javax.naming.directory.InvalidSearchFilterException when the specification of a search
+     * filter is invalid. The expression of the filter may be invalid, or there
+     * may be a problem with one of the parameters passed to the filter.
+     * @throws javax.naming.directory.InvalidSearchControlsException when the specification of the
+     * SearchControls for a search operation is invalid. For example, if the
+     * scope is set to a value other than OBJECT_SCOPE, ONELEVEL_SCOPE,
+     * SUBTREE_SCOPE, this exception is thrown.
+     */
+    public Cursor search(ExprNode a_filter, Name a_baseDn, int a_scope)
+        throws BackendException, NamingException
+    {
+        Cursor l_cursor = new EntryCursor(this,
+            m_searchEngine.search(m_db, a_filter,
+            a_baseDn.toString(), a_scope)) ;
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    /**
+     * Modifies the relative distinguished name (RDN) of an entry
+	 * without changing any parent child relationships.  This call
+	 * has the side effect of altering the distinguished name of
+	 * descendent entries if they exist.  The boolean argument will
+	 * optionally remove the existing RDN attribute value pair
+	 * replacing it with the new RDN attribute value pair.  If other
+	 * RDN attribute value pairs exist besides the current RDN they
+	 * will be spared.
+     *
+     * @param an_entry the entry whose RDN is to be modified.
+     * @param a_newRdn the new Rdn that is to replace the current Rdn.
+     * @param a_deleteOldRdn deletes the old Rdn attribute value pair if true.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    public void modifyRdn( LdapEntry an_entry, Name a_newRdn,
+        boolean a_deleteOldRdn )
+	    throws BackendException, NamingException
+    {
+        m_db.modifyRdn( ( LdapEntryImpl ) an_entry,
+            a_newRdn.toString(), a_deleteOldRdn ) ;
+    }
+
+
+    /**
+     * Moves a child entry without changing the RDN under a new parent
+     * entry.  This effects the parent child relationship between the
+     * parent entry and the child entry.  The index for the child
+     * mapping it to the current parent is destroyed and a new index
+     * mapping it to the new parent is created.  As a side effect the
+     * name of the child entry and all its descendants will reflect the
+     * move within the DIT to a new parent.  The old parent prefix to
+     * the distinguished names of the child and its descendents will be
+     * replaced by the new parent DN prefix.
+     *
+     * @param a_parentEntry the parent the child is to subordinate to.
+     * @param a_childEntry the child to be moved under the parent.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    public void move( LdapEntry a_parentEntry, LdapEntry a_childEntry )
+	    throws BackendException, NamingException
+    {
+        m_db.move( ( LdapEntryImpl ) a_parentEntry, 
+            ( LdapEntryImpl ) a_childEntry) ;
+    }
+
+
+    /**
+     * This overload combines the first two method operations into one.
+     * It changes the Rdn and the parent prefix at the same time while
+	 * recursing name changes to all descendants of the child entry. It
+	 * is obviously more complex than the other two operations alone and
+	 * involves changes to both parent child indices and DN indices.
+     *
+     * @param a_parentEntry the parent the child is to subordinate to.
+     * @param a_childEntry the child to be moved under the parent.
+     * @param a_newRdn the new Rdn that is to replace the current Rdn.
+     * @param a_deleteOldRdn deletes the old Rdn attribute value pair if true.
+     * @throws BackendException when the operation cannot be performed due to a
+     * backing store error.
+     * @throws NamingException when naming violations and or schema violations
+     * occur due to attempting this operation.
+     */
+    public void move( LdapEntry a_parentEntry, LdapEntry a_childEntry,
+	    Name a_newRdn, boolean a_deleteOldRdn )
+	    throws BackendException, NamingException
+    {
+        m_db.move( ( LdapEntryImpl ) a_parentEntry, 
+            ( LdapEntryImpl ) a_childEntry,
+            a_newRdn.toString(), a_deleteOldRdn ) ;
+    }
+
+
+    ///////////////////////////////////////
+    // Remaining Backend Implementations //
+	///////////////////////////////////////
+
+
+    public LdapEntry getParent(Name a_childDN)
+        throws BackendException, NamingException
+    {
+	    return read(a_childDN.getSuffix(1)) ;
+    }
+
+
+    public boolean hasEntry(Name a_dn)
+        throws BackendException, NamingException
+    {
+        return m_db.getEntryId(a_dn.toString())
+            != null ;
+    }
+
+
+    public boolean isSuffix(LdapEntry an_entry)
+        throws NamingException 
+    {
+        return an_entry.getNormalizedDN().equals(m_suffix) ;
+    }
+
+
+    public boolean isAdminUser(Name a_userDn)
+        throws NamingException
+    {
+	    if(a_userDn.equals(m_adminUser)) {
+            return true ;
+        }
+
+        return false ;
+    }
+
+
+    public Cursor listChildren(Name a_parentDN)
+        throws BackendException, NamingException
+    {
+        BigInteger l_id =
+            m_db.getEntryId(a_parentDN.toString()) ;
+
+        if(l_id == null) {
+            throw new NameNotFoundException("Cannot list the children of "
+                + a_parentDN + ".  It does not exist!") ;
+        }
+
+        Cursor l_cursor = new EntryCursor(this, m_db.getChildren(l_id)) ;
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    public String getProperty(String a_propertyName)
+        throws BackendException
+    {
+	    return m_db.getProperty(a_propertyName) ;
+    }
+
+
+    public void setProperty(String a_propertyName, String a_propertyValue)
+        throws BackendException
+    {
+        m_db.setProperty(a_propertyName, a_propertyValue) ;
+    }
+
+
+    public String getImplementationRole()
+    {
+        return ROLE ;
+    }
+
+
+    /**
+     * @phoenix:mx-attribute
+     * @phoenix:mx-description Returns the implementation name.
+     * @phoenix:mx-isWriteable no
+     */
+    public String getImplementationName()
+    {
+        return("Jdbm DB Implementation") ;
+    }
+
+
+    /**
+     * @phoenix:mx-attribute
+     * @phoenix:mx-description Returns the implementation class name.
+     * @phoenix:mx-isWriteable no
+     */
+    public String getImplementationClassName()
+    {
+        return this.getClass().getName() ;
+    }
+
+
+    /**
+     * @phoenix:mx-attribute
+     * @phoenix:mx-description Returns the suffix of this backend.
+     * @phoenix:mx-isWriteable no
+     */
+    public Name getSuffix()
+    {
+        return m_db.getSuffix() ;
+    }
+
+
+    /**
+     * @phoenix:mx-operation
+     * @phoenix:mx-description Synchronizes cached berkeley data with disk.
+     */
+    public void sync()
+        throws BackendException
+    {
+        m_db.sync() ;
+    }
+
+
+    /**
+     * @phoenix:mx-operation
+     * @phoenix:mx-description Invokes the admin tool gui.
+     */
+    public void invokeAdminTool()
+        throws Exception
+    {
+        try {
+			BackendFrame l_frame = new BackendFrame() ;
+            l_frame.enableLogging(getLogger()) ;
+			l_frame.loadDatabase(m_db) ;
+            l_frame.launch() ;
+        } catch(Exception e) {
+            getLogger().error("Got the following exception while trying to "
+                + "invoke the admin tool: " + e.getMessage() + "\n\nTrace:\n"
+                + ExceptionUtil.printStackTrace(e)) ;
+        }
+    }
+
+
+    /////////////////////////////////////////////
+    // Configuration Interface Implementations //
+    /////////////////////////////////////////////
+
+    public static final String INDICES_TAG = "indices" ;
+    public static final String INDEX_TAG = "index" ;
+	public static final String INAME_ATTR = "name" ;
+
+    /**
+     * Avalon Configurable interface implementation specific to a Berkeley Db.
+     * Here's an example configuration with a "cn" and ""dc" index. Notice
+     * that the standard backend configuration attributes are not included:<br>
+     * 
+     *   <config>
+     *		<backend>
+     * 			<indices>
+     * 			<index name="cn"/>
+     * 			<index name="dc"/>
+     * 			</indices>
+     *		</backend>
+     *   </config>
+     *
+     * @param a_config an avalon configuration object for this backend block.
+     * @throws ConfigurationException if the configuration is not correct.
+     */
+    public void configure(Configuration a_config)
+        throws ConfigurationException
+    {
+        super.configure(a_config) ;
+
+        ///////////////////////////////////////////////
+        // Configure Backend Database and Components //
+        ///////////////////////////////////////////////
+
+        try {
+            m_byIdCache.setMaximumSize(m_cache.getMaximumSize()) ;
+            m_searchEngine = new SearchEngine() ;
+            m_searchEngine.enableLogging(getLogger()) ;
+            m_db = new JdbmDatabase (m_schema, m_suffix, m_wkdirPath) ;
+	        m_db.enableLogging(getLogger()) ;
+
+            Configuration [] l_indices =
+	            a_config.getChild(INDICES_TAG).getChildren(INDEX_TAG) ;
+	        String l_attribName = null ;
+	        for(int ii = 0; ii < l_indices.length; ii++) {
+	            l_attribName = l_indices[ii].getAttribute(INAME_ATTR) ;
+                m_db.addIndexOn(l_attribName) ;
+	        }
+        } catch(NamingException e) {
+            throw new ConfigurationException("Could not configure "
+                + getImplementationName() + " with suffix " + m_suffix
+                + " due to NamingException:\n" + e.getMessage(), e) ;
+        } catch(BackendException e) {
+            throw new ConfigurationException("Could not configure "
+                + getImplementationName() + " with suffix " + m_suffix
+                + " due to NamingException:\n" + e.getMessage(), e) ;
+        }
+    }
+
+
+    public void stop()
+        throws Exception
+    {
+        super.stop() ;
+        m_db.close() ;
+    }
+
+
+    /**
+     * @phoenix:dependency name="org.apache.eve.schema.SchemaManager"
+     * @phoenix:dependency name="org.apache.eve.backend.UnifiedBackend"
+     */
+    public void service(ServiceManager a_manager)
+        throws ServiceException
+    {
+        super.service(a_manager) ;
+        m_schemaManager =
+            (SchemaManager) a_manager.lookup(SchemaManager.ROLE) ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/LdapEntryImpl.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/LdapEntryImpl.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,850 @@
+/*
+ * $Id: LdapEntryImpl.java,v 1.9 2003/08/06 03:01:25 akarasulu Exp $
+ * $Prologue$
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm ;
+
+
+import java.util.Date ;
+import java.util.HashMap ;
+import java.util.Iterator ;
+import java.util.Collection ;
+import java.math.BigInteger ;
+
+import javax.naming.Name ;
+import javax.naming.NamingException ;
+import javax.naming.directory.AttributeInUseException ;
+import javax.naming.directory.InvalidAttributeValueException ;
+import javax.naming.directory.AttributeModificationException ;
+import javax.naming.directory.InvalidAttributeIdentifierException ;
+
+import org.apache.ldap.common.util.NamespaceTools ;
+import org.apache.eve.backend.LdapEntry ;
+import org.apache.eve.schema.Schema ;
+import org.apache.eve.protocol.ProtocolModule;
+
+import org.apache.avalon.framework.logger.Logger ;
+import org.apache.avalon.framework.logger.LogEnabled ;
+
+import org.apache.commons.collections.LRUMap ;
+import org.apache.commons.collections.MultiHashMap ;
+import org.apache.ldap.common.name.DnParser;
+
+
+/**
+ * An entry implementation for the Berkeley backend.
+ * 
+ * @task No schema checking going on right now - but this needs to change!
+ * @task Also we need to make sure that we are storing dates in the apropriate
+ * fashion
+ */
+public class LdapEntryImpl
+    extends MultiHashMap
+	implements LdapEntry, LogEnabled
+{
+    private final Schema m_schema ;
+    private Logger m_log ;
+    private Name m_normalizedDn ;
+    private Name m_dn ;
+    private boolean m_isValid = false ;
+    private BigInteger m_id ;
+    private HashMap m_attrIdMap = new HashMap() ;
+    private LRUMap m_attrIdCache = new LRUMap(25) ;
+
+    // Only used on validated entries - validation initializes them.
+    private MultiHashMap m_added = null ;
+    private MultiHashMap m_removed = null ;
+
+
+	LdapEntryImpl( Schema a_schema, Name a_dName, String a_dnStr )
+    {
+        m_schema = a_schema ;	       				// schema this entry uses
+        m_normalizedDn = a_dName ;	    			// normalized parsed name
+        put( DN_ATTR, a_dnStr ) ;	    			// user provided name
+    }
+
+
+    LdapEntryImpl(Schema a_schema)
+    {
+        m_schema = a_schema ;
+    }
+
+
+    /**
+     * Note that according to the Backend-Server contract this method must
+     * return a Collection of attribute identifier names as they were
+     * specified by the user at creation time.  These are not normalized
+     * attribute identifiers.
+     */
+    public Collection attributes()
+    {
+        return this.keySet() ;
+    }
+
+
+    public Schema getSchema()
+    {
+        return m_schema ;
+    }
+
+
+	public boolean equals(String a_dn)
+        throws NamingException
+    {
+        String l_normalized = m_schema.normalize(Schema.DN_ATTR, a_dn) ;
+        Name l_name = this.getNormalizedDN() ;
+        return l_name.toString().equals(l_normalized) ;
+    }
+
+
+    public boolean hasAttribute(String an_attributeName)
+    {
+        return containsKey(an_attributeName) ;
+    }
+
+
+    public boolean hasAttributeValuePair(String an_attribute, Object a_value)
+    {
+        if(containsKey(an_attribute)) {
+            Collection l_collection = getMultiValue(an_attribute) ;
+
+            if(null == l_collection && null == a_value) {
+                return true ;
+            } else if(null == l_collection || null == a_value) {
+                return false ;
+            } else if(l_collection.contains(a_value)) {
+                return true ;
+            } else {
+                return false ;
+            }
+        } else {
+	    return false ;
+        }
+    }
+
+
+    /**
+     * Gets this entry's creation timestamp as a Date.
+     *
+     * @return Date representing the creation timestamp of this entry.
+     */
+    public Date getCreateTimestamp()
+    {
+	return new Date((String) getSingleValue(CREATETIMESTAMP_ATTR)) ;
+    }
+
+
+    /**
+     * Gets the distinguished name of the creator of this entry.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     * 
+     * @return the distinguished name of the creator.
+     */
+    public String getCreatorsName()
+    {
+        return (String) getSingleValue(CREATORSNAME_ATTR) ;
+    }
+
+
+    /**
+     * Gets the unique distinguished name associated with this entry as it was
+     * supplied during creation without whitespace trimming or character case
+     * conversions.  This version of the DN is kept within the body of this
+     * Entry as an operational attribute so that it could be returned as it was
+     * given to the server w/o normalization effects: case and whitespace will
+     * be entact.
+     *
+     * @return the distinguished name of this entry as a String.
+     */
+    public String getEntryDN()
+    {
+        return (String) getSingleValue(DN_ATTR) ;
+    }
+
+
+    /**
+     * Gets the unique identifier of this entry as a BigInteger.
+     *
+     * @return BigInteger unique identifier of this entry.
+     */
+    public BigInteger getEntryID()
+    {
+        if(m_id == null) {
+            m_id = new BigInteger((String) getSingleValue(ID_ATTR)) ;
+        }
+
+        return m_id ;
+    }
+
+
+    /**
+     * Gets the distinguished name of the last modifier of this entry.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     * 
+     * @return the DN of the user to modify this entry last.
+     */
+    public String getModifiersName()
+    {
+        return (String) getSingleValue(MODIFIERSNAME_ATTR) ;
+    }
+
+
+    /**
+     * Gets this entry's modification timestamp as a Date.
+     *
+     * @return Date representing the timestamp this entry was last modified.
+     */
+    public Date getModifyTimestamp()
+    {
+        return new Date((String)
+            getSingleValue(MODIFYTIMESTAMP_ATTR)) ;
+    }
+
+
+    /**
+     * Gets the normalized unique distinguished name associated with this 
+     * entry. This DN unlike the user specified DN accessed via getDN() is not
+     * an operational attribute composing the body of this Entry.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     *
+     * @return the normalized distinguished name of this entry as a String.
+     */
+    public Name getNormalizedDN()
+        throws NamingException
+    {
+        if(m_normalizedDn == null) {
+            m_normalizedDn =
+                m_schema.getNormalizingParser().parse(getEntryDN()) ;
+        }
+
+        return m_normalizedDn;
+    }
+
+
+    /**
+     * Gets a Name representation of the originally user provided name without
+     * any case conversions.  I added this method so we can cache this value
+     * and avoid the reparse via the use of the nexus getName method.
+     */
+    public Name getUnNormalizedDN()
+        throws NamingException
+    {
+        if(m_dn == null) {
+            m_dn = m_schema.getNameParser().parse(getEntryDN()) ;
+        }
+
+        return m_dn ;
+    }
+
+
+    /**
+     * Gets the number of subordinate child entries that exist for this Entry.
+     *
+     * @return the number of subordinates (children).
+     */
+    public BigInteger getNumSubordinates()
+    {
+        return new BigInteger((String)
+            getSingleValue(NUMSUBORDINATES_ATTR)) ;
+    }
+
+
+    /**
+     * Gets the normalized unique distinguished name of this Entry's parent.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     *
+     * @return the normalized distinguished name of this Entry's parent.
+     */
+    public String getParentDN()
+    {
+        return (String) getSingleValue(PARENTDN_ATTR) ;
+    }
+
+
+    /**
+     * Gets the unique identifier of this entry's parent entry as a BigInteger.
+     * The parent ID is equal to the entry id <code>
+     * (getEntryID().equals(parentID()) == true) </code> if this entry is a 
+     * root (a.k.a. suffix) entry.
+     *
+     * @return the uid of this entry's parent.
+     */
+    public BigInteger getParentID()
+    {
+        return new BigInteger((String) getSingleValue(PARENTID_ATTR)) ;
+    }
+
+
+    /**
+     * Gets the distinguished name of the subschema subentry for this Entry.
+     *
+     * The distinguished name is presumed to be normalized by the server naming
+     * subsystem in accordance with schema attribute syntax and attribute
+     * matching rules. The DN is also presumed to be syntacticly correct and
+     * within the namespace of this directory information base from which this
+     * Entry originated.
+     * 
+     * @return String of the subschema subentry distinguished name.
+     */
+    public String getSubschemaSubentryDN()
+    {
+        return (String) getSingleValue(SUBSCHEMASUBENTRY_ATTR) ;
+    }
+
+
+    /**
+     * Checks whether or not this Entry is a valid entry residing within a
+     * backend.  Entries are validated on successful create() calls.  When
+     * Entry instances are initialized in the java sense via the newEntry()
+     * call on backends, they are in the invalid state.
+     *
+     * @return true if this entry has been persisted to a backend,
+     * false otherwise.
+     */
+    public boolean isValid()
+    {
+        return m_isValid ;
+    }
+
+
+    /**
+     * Gets a multivalued attribute by name.
+     *
+     * @param a_attribName the name of the attribute to lookup.
+     * @return a Collection or null if no attribute value exists.
+     */
+    public Collection getMultiValue(String an_attribName)
+    {
+        return (Collection) get(an_attribName) ;
+    }
+
+
+    /**
+     * Gets a single valued attribute by name or returns the first value of a
+     * multivalued attribute.
+     *
+     * @param a_attribName the name of the attribute to lookup.
+     * @return an Object value which is either a String or byte [] or null if
+     * the attribute does not exist.
+     */
+    public Object getSingleValue(String an_attribName)
+    {
+        Collection l_col = (Collection) get(an_attribName) ;
+
+		if(null == l_col || l_col.isEmpty()) {
+            return null ;
+        }
+
+        return l_col.iterator().next() ;
+    }
+
+
+    private void checkIdentifier( String an_attribName )
+        throws InvalidAttributeIdentifierException
+    {
+		if( ! m_schema.hasAttribute( an_attribName ) )
+        {
+            // InvalidAttributeIdentifierException JNDI exceptions corresponds
+            // to an UNDEFINEDATTRIBUTETYPE result code within LDAPv2 with a
+            // value of [17]
+            throw new InvalidAttributeIdentifierException(
+                "[17] " + an_attribName +
+                " is not a valid schema recognized attribute name." ) ;
+        }
+    }
+
+
+    /**
+     * Adds a value to this Entry potentially resulting in more than one value
+     * for the attribute/key.
+     * 
+     * @param an_attribName attribute name/key
+     * @param a_value the value to add
+     * @throws InvalidAttributeIdentifierException when an attempt is made to
+     * add to or create an attribute with an invalid attribute identifier.
+     * @throws InvalidAttributeValueException when an attempt is made to add to
+     * an attribute a value that conflicts with the attribute's schema
+     * definition. This could happen, for example, if attempting to add an
+     * attribute with no value when the attribute is required to have at least
+     * one value, or if attempting to add more than one value to a single
+     * valued-attribute, or if attempting to add a value that conflicts with 
+     * the syntax of the attribute.
+     */
+    public void addValue(String an_attribName, Object a_value)
+        throws
+        AttributeInUseException,
+        InvalidAttributeValueException,
+        InvalidAttributeIdentifierException
+    {
+        // Null args have no affect
+	    if( null == a_value || null == an_attribName )
+        {
+            return ;
+        }
+
+		checkIdentifier( an_attribName ) ;
+
+		if( containsKey( an_attribName ) &&
+            m_schema.isSingleValue( an_attribName ) )
+        {
+            // AttributeInUseException JNDI exceptions correspond to
+            // ATTRIBUTEORVALUEEXISTS result code in LDAPv3 with a value of [20]
+            throw new AttributeInUseException( "[20] A key for attribute "
+                + an_attribName + " already exists!" ) ;
+        }
+
+
+        // Indices do not exist on binary attributes so there is no need to
+        // update them or for that matter track changes to them
+        if( m_schema.isBinary( an_attribName ) )
+        {
+            put( an_attribName, a_value ) ;
+        }
+
+        // @todo Answer why we are not asking the schema here if the value is
+        // valid when we are dealing with numbers? Shouldn't we be performing
+        // some form of syntax check?
+        else if( m_schema.isNumeric( an_attribName ) ||
+            m_schema.isDecimal( an_attribName ) )
+        {
+            put( an_attribName, a_value ) ;
+
+            if( m_isValid )
+            {
+                m_added.put( an_attribName, a_value ) ;
+            }
+        }
+        else
+        {
+            String l_value = ( String ) a_value ;
+
+    	    if( l_value.trim().equals( "" ) )
+    		{
+                return ;
+            }
+
+            if( ! m_schema.isValidSyntax( an_attribName, l_value ) )
+            {
+                // InvalidAttributeValueException JNDI exceptions correspond to
+                // the INVALIDATTRIBUTESYNTAX result code with a value of [21]
+                throw new InvalidAttributeValueException( "[21] '" + l_value
+                    + "' does not comply with the syntax for attribute "
+                    + an_attribName ) ;
+            }
+
+            put( an_attribName, l_value ) ;
+
+            if( m_isValid )
+            {
+                m_added.put( an_attribName, l_value ) ;
+            }
+        }
+    }
+
+
+    /**
+     * Removes the attribute/value pair in this Entry only without affecting
+     * other values that the attribute may have.
+     *
+     * @param an_attribName attribute name/key
+     * @param a_value the value to remove
+     * @throws AttributeModificationException when an attempt is made to modify
+     * an attribute, its identifier, or its values that conflicts with the
+     * attribute's (schema) definition or the attribute's state.  Also thrown
+     * if the specified attribute name does not exist as a key in this Entry.
+     */
+    public void removeValue( String an_attribName, Object a_value )
+        throws InvalidAttributeIdentifierException
+    {
+		checkIdentifier( an_attribName ) ;
+
+        if( m_schema.isBinary( an_attribName ) )
+        {
+            remove( an_attribName, a_value ) ;
+        }
+        else
+        {
+            remove( an_attribName, a_value ) ;
+
+        	// For valid entries within the backend we track what we added and
+            // removed.  Need to remove values added to shadow copy that now get
+            // removed.
+            if( m_isValid )
+            {
+                m_removed.put( an_attribName, a_value ) ;
+                m_added.remove( an_attribName, a_value ) ;
+            }
+        }
+    }
+
+
+    /**
+     * Removes all the attribute/value pairs in this Entry associated with the
+     * attribute.
+     *
+     * @param an_attribName attribute name/key
+     * @param a_value the value to remove
+     * @throws AttributeModificationException when an attempt is made to modify
+     * an attribute, its identifier, or its values that conflicts with the
+     * attribute's (schema) definition or the attribute's state.  Also thrown
+     * if the specified attribute name does not exist as a key in this Entry.
+     */
+    public void removeValues( String an_attribName )
+        throws InvalidAttributeIdentifierException
+    {
+		checkIdentifier( an_attribName ) ;
+
+        // Don't care if the value does not exist
+        if( ! containsKey( an_attribName ) )
+        {
+            return ;
+        }
+
+        Iterator l_removed = getMultiValue( an_attribName ).iterator() ;
+        if( m_isValid )
+        {
+			while( l_removed.hasNext() )
+            {
+					m_removed.put( an_attribName, l_removed.next() ) ;
+			}
+	
+			m_added.remove( an_attribName ) ;
+		}
+
+		remove( an_attribName ) ;
+    }
+
+
+    /**
+     * Removes the specified set of attribute/value pairs in this Entry.
+     *
+     * @param an_attribName attribute name/key
+     * @param a_valueArray the set of values to remove
+     * @throws AttributeModificationException when an attempt is made to modify
+     * an attribute, its identifier, or its values that conflicts with the
+     * attribute's (schema) definition or the attribute's state.  Also thrown
+     * if the specified attribute name does not exist as a key in this Entry.
+     */
+    public void removeValues(String an_attribName, Object [] a_valueArray)
+        throws InvalidAttributeIdentifierException
+    {
+		checkIdentifier( an_attribName ) ;
+
+        for( int ii = 0; ii < a_valueArray.length; ii++ )
+        {
+            remove( an_attribName, a_valueArray[ii] ) ;
+        }
+    }
+
+
+    //////////////////////////////
+    // Package Friendly Methods //
+    //////////////////////////////
+
+
+    /**
+     * This method is called to indicate that this entry has now been created
+     * within a backend.  Any alterations to be made to is are going to be
+     * tracked using the three multimaps.  The add, remove and change methods
+     * will on use now begin to add attribute into the appropriate multimap
+     * resouvoirs to enable store optimization on updates.
+     */
+    void validate()
+    {
+        m_isValid = true ;
+
+		if(null == m_added) {
+	    	m_added = new MultiHashMap() ;
+        } else {
+            m_added.clear() ;
+        }
+
+        if(null == m_removed) {
+	    	m_removed = new MultiHashMap() ;
+        } else {
+            m_removed.clear() ;
+        }
+    }
+
+
+    MultiHashMap getRemoved()
+    {
+        return m_removed ;
+    }
+
+    
+    MultiHashMap getAdded()
+    {
+        return m_added ;
+    }
+
+
+    /**
+     * Sets the parent id and parent dn operational attributes of this 
+     * entry to correspond to a new parent entry.  The appropriate changes
+     * are made to the added and removed multimaps to correctly update
+     * this entry with the new parent child relationship.  Effects are not
+     * manifested in the backend util an update is performed on this Entry.
+     *
+     * @param a_parentEntry the parent to make this Entry a child of.
+     */
+    void setParent( LdapEntry a_parentEntry )
+        throws NamingException
+    {
+		removeValues( PARENTDN_ATTR ) ;
+		addValue(PARENTDN_ATTR, a_parentEntry.getEntryDN() ) ;
+	
+		removeValues( PARENTID_ATTR ) ;
+		addValue( PARENTID_ATTR, ( ( LdapEntryImpl ) a_parentEntry )
+            .getSingleValue( ID_ATTR ) ) ;
+    }
+
+
+    /**
+     * Sets this Entry's DN modifying both the operational attribute for 
+     * tracking the user provided DN as well as the normalized DN.
+     *
+     * @param a_dn the new user provided dn to change this Entry's DN to.
+     */
+    void setEntryDN(Name a_dn)
+        throws NamingException
+    {
+        if(getLogger().isDebugEnabled()) {
+            getLogger().debug("" //ProtocolModule.getMessageKey()
+                + " - LdapEntryImpl.setEntryDN() - altering entry dn from "
+                + m_normalizedDn+ " to " + a_dn) ;
+        }
+
+        // Get the old user provided Dn and remove it by removing it from
+        // this MultiMap and adding it to the m_removed MultiMap.
+        String l_oldDn = getEntryDN() ;
+        remove(DN_ATTR, l_oldDn) ;
+        if(m_isValid) {
+            m_removed.put(DN_ATTR, l_oldDn) ;
+        }
+
+        // Reparse as normalized name using schema's normalizing parser
+		m_normalizedDn =
+            m_schema.getNormalizingParser().parse(a_dn.toString()) ;
+
+        // Add unnormalized user provided Dn argument to this MultiMap and
+        // the m_added MultiMap
+        put(DN_ATTR, a_dn.toString()) ;
+        if(this.m_isValid) {
+            m_added.put(DN_ATTR, a_dn.toString()) ;
+        }
+
+        if(getLogger().isDebugEnabled()) {
+            getLogger().debug("" //ProtocolModule.getMessageKey()
+                + " - LdapEntryImpl.setEntryDN() - dumping delta hashes "
+                + "after removing the old dn and adding the new Dn") ;
+            getLogger().debug("Added hash:\n" + this.m_added.toString()) ;
+            getLogger().debug("Removed hash:\n" + this.m_removed.toString()) ;
+        }
+    }
+
+
+    ////////////////////////////////////
+    // Overridden MultiMap Interfaces //
+    ////////////////////////////////////
+
+
+    /**
+     * Gets the user provided key form of the attribute argument first then
+     * calls the super class' containsKey with the calculated UPK.  If no
+     * UPK is found then false is returned.
+     */
+    public boolean containsKey(Object a_attrId)
+    {
+        String l_upk = getUserProvidedKey((String) a_attrId) ;
+
+        if(l_upk == null) {
+            return false ;
+        }
+
+        return super.containsKey(l_upk) ;
+    }
+
+
+    /**
+     * Gets the user provided key form of the attribute argument first then
+     * calls the super class' get with the calculated UPK.  If no UPK is found
+     * then null is returned.
+     */
+    public Object get(Object a_attrId)
+    {
+        String l_upk = getUserProvidedKey((String) a_attrId) ;
+
+        if(l_upk == null) {
+            return null ;
+        }
+
+        return super.get(l_upk) ;
+    }
+
+
+    /**
+     * Gets the user provided key form of the attribute argument first then
+     * calls the super class' put with the calculated UPK.  If there is no
+     * UPK defined it persumes that the argument is the UPK adding it to the
+     * map of canonicial forms to UPKs.  So first come first serve - meaning
+     * the first time an attribute is set to a value the text form of the
+     * attribute identifier is set as the UPK.
+     */
+    public Object put(Object a_attrId, Object a_value)
+    {
+        String l_upk = getUserProvidedKey((String) a_attrId) ;
+
+        if(l_upk == null) {
+            setUserProvidedKey((String) a_attrId) ;
+            l_upk = (String) a_attrId ;
+        }
+
+        return super.put(l_upk, a_value) ;
+    }
+
+
+    /**
+     * Gets the user provided key form of the attribute argument first then
+     * calls the super class' remove with the calculated UPK.  If no UPK is
+     * found then nothing is done.
+     */
+    public Object remove(Object a_attrId)
+    {
+        String l_upk = getUserProvidedKey((String) a_attrId) ;
+
+        if(l_upk == null) {
+            return null ;
+        }
+
+        return super.remove(l_upk) ;
+    }
+
+
+    /**
+     * Gets the user provided key form of the attribute argument first then
+     * calls the super class' remove with the calculated UPK.  If no UPK is
+     * found then nothing is done.
+     */
+    public Object remove(Object a_attrId, Object a_value)
+    {
+        String l_upk = getUserProvidedKey((String) a_attrId) ;
+
+        if(l_upk == null) {
+            return null ;
+        }
+
+        return super.remove(l_upk, a_value) ;
+    }
+
+
+    /////////////////////
+    // Utility Methods //
+    /////////////////////
+
+
+    /**
+     * Gets the canonical text normalized form of the attribute identifier. It
+     * first tests to see if the canonical for exists in the normalized
+     * attribute id cache.  If the cannonical form is there it is returned
+     * other wise it generates the normalized form and adds it to the cache.
+     * It then returns this newly generated normalized form.
+     */
+    private String getCanonicalKey(String a_attrId)
+    {
+        String l_canonical = null ;
+
+        // Now if the argument is not the key get the canonical key either
+        // from the attribute id cache or by creating it from scratch.
+        if(m_attrIdCache.containsKey(a_attrId)) {
+            l_canonical = (String) m_attrIdCache.get(a_attrId) ;
+        } else {
+            l_canonical = a_attrId.toLowerCase() ;
+            m_attrIdCache.put(a_attrId, l_canonical) ;
+            m_attrIdCache.put(l_canonical, l_canonical) ;
+        }
+
+        return l_canonical ;
+    }
+
+
+    /**
+     * Gets the original user provided key as was entered into the backend the
+     * first time.  It first presumes that the argument a_attrId is already \
+     * normalized by attempting a lookup into the normalized attribute id to
+     * user defined attribute id map.  If it finds it there the value is
+     * returned.  Otherwise it calls getCanonicalKey on the argument to get
+     * the canonical form and used the cannonical form for the same lookup. If
+     * nothing is found in the id map then null is returned.
+     */
+    private String getUserProvidedKey(String a_attrId)
+    {
+        if(m_attrIdMap.containsKey(a_attrId)) {
+            return (String) m_attrIdMap.get(a_attrId) ;
+        } else {
+            String l_canonical = getCanonicalKey(a_attrId) ;
+            if(m_attrIdMap.containsKey(l_canonical)) {
+                return (String) m_attrIdMap.get(l_canonical) ;
+            }
+        }
+
+        return null ;
+    }
+
+
+    /**
+     * Sets the user defined key in the user defined attribute Id map by making
+     * a call to getCanonicalKey to get the text normalized version of the
+     * argument String a_upk.  It uses this normalized form as the key and the
+     * argument String a_upk to create a new entry in the attribute id map.
+     */
+    private void setUserProvidedKey(String a_upk)
+    {
+        String l_canonical = getCanonicalKey(a_upk) ;
+        m_attrIdMap.put(l_canonical, a_upk) ;
+    }
+
+
+    public void enableLogging(Logger a_logger)
+    {
+        m_log = a_logger ;
+    }
+
+
+    public boolean isDebugEnabled()
+    {
+        return m_log.isDebugEnabled() ;
+    }
+
+
+    public Logger getLogger()
+    {
+        return m_log ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/ASTNode.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/ASTNode.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,121 @@
+/*
+ * $Id: ASTNode.java,v 1.2 2003/03/13 18:27:19 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.gui ;
+
+
+import java.util.ArrayList ;
+import java.util.Enumeration ;
+import javax.swing.tree.TreeNode ;
+
+import org.apache.eve.backend.jdbm.Database ;
+import org.apache.eve.backend.jdbm.LdapEntryImpl ;
+import org.apache.eve.backend.jdbm.index.IndexRecord;
+import java.util.Collections;
+import org.apache.eve.backend.Cursor;
+import java.util.Iterator;
+import org.apache.ldap.common.filter.ExprNode;
+import org.apache.ldap.common.filter.BranchNode;
+import org.apache.ldap.common.filter.LeafNode;
+import org.apache.ldap.common.filter.PresenceNode;
+import org.apache.ldap.common.filter.SubstringNode;
+
+
+/**
+ * A node representing an entry.
+ */
+public class ASTNode
+	implements TreeNode
+{
+    private final ASTNode m_parent ;
+	private final ExprNode m_exprNode ;
+    private final ArrayList m_children ;
+
+
+    public ASTNode(ASTNode a_parent, ExprNode a_exprNode)
+    {
+        m_children = new ArrayList(2) ;
+        m_exprNode = a_exprNode ;
+
+        if(a_parent == null) {
+            m_parent = this ;
+        } else {
+            m_parent = a_parent ;
+        }
+
+		try {
+            if(m_exprNode.isLeaf()) {
+                return ;
+            }
+
+            BranchNode l_branch = (BranchNode) m_exprNode ;
+            ArrayList l_exprNodes = l_branch.getChildren() ;
+            for(int ii = 0 ; ii < l_exprNodes.size(); ii++) {
+                ExprNode l_child = (ExprNode) l_exprNodes.get(ii) ;
+                m_children.add(new ASTNode(this, l_child)) ;
+            }
+        } catch(Exception e) {
+            e.printStackTrace() ;
+        }
+    }
+
+
+    public Enumeration children()
+    {
+        return Collections.enumeration(m_children) ;
+    }
+
+
+    public boolean getAllowsChildren()
+    {
+        return !m_exprNode.isLeaf() ;
+    }
+
+
+    public TreeNode getChildAt(int a_childIndex)
+    {
+		return (TreeNode) m_children.get(a_childIndex) ;
+    }
+
+
+    public int getChildCount()
+    {
+        return m_children.size() ;
+    }
+
+
+    public int getIndex(TreeNode a_child)
+    {
+        return m_children.indexOf(a_child) ;
+    }
+
+
+    public TreeNode getParent()
+    {
+        return m_parent ;
+    }
+
+
+    public boolean isLeaf()
+    {
+        return m_children.size() <= 0 ;
+    }
+
+
+    public String toString()
+    {
+        return m_exprNode.toString() ;
+    }
+
+
+    public ExprNode getExprNode()
+    {
+        return m_exprNode ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/AboutDialog.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/AboutDialog.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,148 @@
+/*
+ * $Id: AboutDialog.java,v 1.2 2003/03/13 18:27:20 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.gui;
+
+import java.awt.GridBagLayout;
+import java.awt.GridBagConstraints;
+import java.awt.Insets;
+import javax.swing.JPanel;
+import javax.swing.JLabel;
+import javax.swing.JButton;
+import javax.swing.JTextArea;
+import javax.swing.BorderFactory;
+import javax.swing.JDialog;
+import java.awt.Frame;
+import java.awt.Rectangle;
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.WindowEvent;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class AboutDialog extends JDialog {
+    private String title = "About";
+    private String product = "ldapd-modjdbm backend";
+    private String version = "0.7";
+    private String copyright = "Copyright (c) 2003";
+    private String comments =
+        "This is the jdbm backend database viewer or introspector.\n" +
+        "Irregular behavior can be analyzed by using this tool to inspect\n" +
+        "the state of system indices and entry attributes within the backend." ;
+    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), "Ldapd Project", 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("Ldapd");
+        image.setIcon(
+        new javax.swing.ImageIcon("C:/projects/ldapd-modjdbm/src/java/ldapd/server/backend/modjdbm/gui/ldapd.gif"));
+        image.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
+        image.setMinimumSize(new java.awt.Dimension(98,44));
+        image.setMaximumSize(new java.awt.Dimension(98,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(98,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) {
+        setVisible(false);
+        dispose();
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/AnnotatedFilterTreeDialog.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/AnnotatedFilterTreeDialog.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,112 @@
+/*
+ * $Id: AnnotatedFilterTreeDialog.java,v 1.2 2003/03/13 18:27:21 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.gui ;
+
+import javax.swing.JDialog ;
+import java.awt.Frame ;
+import java.awt.event.WindowEvent ;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.JTextArea;
+import javax.swing.JButton;
+import javax.swing.tree.TreeModel;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+
+
+public class AnnotatedFilterTreeDialog
+    extends JDialog
+{
+    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) {
+        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) ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/BackendFrame.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/BackendFrame.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,742 @@
+/*
+ * $Id: BackendFrame.java,v 1.3 2003/03/13 18:27:22 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.gui ;
+
+
+import java.io.File ;
+
+import javax.swing.JTree ;
+import javax.swing.JMenu ;
+import javax.swing.JTable ;
+import javax.swing.JFrame ;
+import javax.swing.JPanel ;
+import javax.swing.JLabel ;
+import javax.swing.JMenuBar ;
+import javax.swing.JMenuItem ;
+import javax.swing.JSplitPane ;
+import javax.swing.JSeparator ;
+import javax.swing.JScrollPane ;
+import javax.swing.JTabbedPane ;
+import javax.swing.JFileChooser ;
+import javax.swing.tree.TreePath ;
+import javax.swing.tree.TreeModel ;
+import javax.swing.tree.DefaultTreeModel ;
+import javax.swing.event.TreeSelectionEvent ;
+import javax.swing.event.TreeSelectionListener ;
+
+import java.awt.Dimension ;
+import java.awt.BorderLayout ;
+import java.awt.event.ActionEvent ;
+import java.awt.event.ActionListener ;
+
+import org.apache.eve.backend.jdbm.Database ;
+import org.apache.eve.backend.jdbm.LdapEntryImpl ;
+
+import org.apache.eve.Kernel ;
+import java.util.HashMap;
+import java.awt.Toolkit;
+import org.apache.eve.backend.jdbm.search.SearchEngine;
+import org.apache.eve.backend.jdbm.JdbmModule;
+import org.apache.eve.backend.Backend;
+import org.apache.ldap.common.filter.FilterParserImpl;
+import org.apache.ldap.common.filter.FilterParser;
+import org.apache.ldap.common.filter.ExprNode;
+import org.apache.ldap.common.filter.FilterParserMonitorAdapter;
+
+import javax.swing.JOptionPane;
+import java.util.Iterator;
+import org.apache.eve.backend.jdbm.index.Index;
+import javax.swing.tree.TreeNode;
+import org.apache.eve.backend.jdbm.search.DefaultOptimizer;
+import javax.swing.JTextArea;
+import java.util.Stack;
+import java.math.BigInteger;
+import org.apache.eve.backend.jdbm.index.IndexRecord;
+import javax.swing.table.DefaultTableModel;
+import org.apache.eve.backend.Cursor;
+import org.apache.avalon.framework.logger.LogEnabled;
+import org.apache.avalon.framework.logger.Logger;
+
+
+public class BackendFrame
+    extends JFrame
+    implements LogEnabled
+{
+    private JLabel statusBar = new JLabel("Ready") ;
+    private JPanel m_mainPnl = new JPanel() ;
+    private JSplitPane m_splitPane = new JSplitPane() ;
+    private JTabbedPane m_tabbedPane = new JTabbedPane() ;
+    private JPanel m_entryPnl = new JPanel() ;
+    private JPanel m_idxPnl = new JPanel() ;
+    private JScrollPane m_treePane = new JScrollPane() ;
+    private JTree m_tree = new JTree() ;
+    private JScrollPane m_entryPane = new JScrollPane() ;
+    private JTable m_entryTbl = new JTable() ;
+    private JScrollPane m_idxPane = new JScrollPane() ;
+    private JTable m_idxTbl = new JTable() ;
+    private JMenu m_searchMenu = new JMenu();
+    private JMenuItem m_annotate = new JMenuItem();
+    private JMenuItem m_run = new JMenuItem();
+    private JMenuItem m_debug = new JMenuItem();
+    private JMenu m_indices = new JMenu();
+    private Database m_database = null ;
+    private boolean m_doCleanUp = false ;
+    private HashMap m_nodes = new HashMap() ;
+    private EntryNode m_root = null ;
+    private Kernel m_kernel = null ;
+    private Logger m_logger = null ;
+
+    /**
+     * Creates new form JFrame
+     */
+    public BackendFrame() {
+        initGUI() ;
+        pack() ;
+    }
+
+
+    /**
+     * This method is called from within the constructor to initialize the form
+     */
+    private void initGUI() {
+        m_mainPnl.setBorder(null) ;
+        m_mainPnl.setLayout(new java.awt.BorderLayout()) ;
+        m_mainPnl.add(m_splitPane, java.awt.BorderLayout.CENTER) ;
+        m_splitPane.add(m_tabbedPane, javax.swing.JSplitPane.RIGHT) ;
+        m_splitPane.add(m_treePane, javax.swing.JSplitPane.LEFT) ;
+        m_tabbedPane.add(m_entryPnl, "Entry Attributes") ;
+        m_tabbedPane.add(m_idxPnl, "Entry Indices") ;
+
+        m_entryPnl.setLayout(new java.awt.BorderLayout());
+        m_entryPnl.add(m_entryPane, java.awt.BorderLayout.CENTER);
+
+        m_idxPnl.setLayout(new java.awt.BorderLayout());
+        m_idxPnl.add(m_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(m_mainPnl, java.awt.BorderLayout.NORTH);
+        getContentPane().add(content, BorderLayout.CENTER);
+        // set title
+        setTitle("Jdbm DB Backend Viewer");
+        // add status bar
+        getContentPane().add(statusBar, BorderLayout.SOUTH);
+        // add menu bar
+        JMenuBar menuBar = new JMenuBar();
+        JMenu m_backendMenu = new JMenu("File");
+        m_backendMenu.setMnemonic('F');
+        // create Exit menu item
+        JMenuItem m_exit = new JMenuItem("Exit");
+        m_exit.setMnemonic('E');
+        m_exit.setBackground(new java.awt.Color(205,205,205));
+        m_exit.addActionListener(
+            new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    exitForm() ;
+                }
+            });
+        // create About menu item
+        JMenu m_helpMenu = new JMenu("Help");
+        m_helpMenu.setMnemonic('H');
+        JMenuItem m_about = new JMenuItem("About");
+        m_about.setMnemonic('A');
+        m_about.setBackground(new java.awt.Color(205,205,205));
+        m_about.addActionListener(
+            new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    AboutDialog aboutDialog = new AboutDialog (BackendFrame.this, true);
+                    Dimension frameSize = getSize();
+                    Dimension aboutSize = aboutDialog.getPreferredSize();
+                    int x = getLocation().x + (frameSize.width - aboutSize.width) / 2;
+                    int y = getLocation().y + (frameSize.height - aboutSize.height) / 2;
+                    if (x < 0) x = 0;
+                    if (y < 0) y = 0;
+                    aboutDialog.setLocation(x, y);
+                    aboutDialog.setVisible(true);
+                }
+            });
+        m_helpMenu.setBackground(new java.awt.Color(205,205,205));
+        m_helpMenu.add(m_about);
+        // create Open menu item
+        String l_startPath = null ;
+        if(File.separatorChar == '/') {
+            l_startPath = "../projects/ldapd-test" ;
+        } else {
+            l_startPath = "..\\projects\\ldapd-test" ;
+        }
+
+        final JFileChooser fc = new JFileChooser(l_startPath);
+        JMenuItem m_open = new JMenuItem("Open");
+        m_open.setMnemonic('O');
+        m_open.setName("Open");
+        m_open.setActionCommand("Open");
+        m_open.setBackground(new java.awt.Color(205,205,205));
+        m_open.addActionListener(
+            new java.awt.event.ActionListener() {
+                public void actionPerformed(java.awt.event.ActionEvent e) {
+                    int returnVal = fc.showOpenDialog(BackendFrame.this);
+                    if (returnVal == javax.swing.JFileChooser.APPROVE_OPTION) {
+                        File file = fc.getSelectedFile() ;
+                        try {
+							loadDatabase("ldapd.test.Harness", file.getParent()) ;
+                        } catch(Exception ex) {
+                            ex.printStackTrace() ;
+                        }
+                    } else {
+                        // Write your code here what to do if user has canceled Open dialog
+                    }
+                }
+            });
+        m_backendMenu.setName("Backend");
+        m_backendMenu.setBackground(new java.awt.Color(205,205,205));
+        m_backendMenu.add(m_open);
+        // create Save menu item
+        // create Print menu item
+        m_backendMenu.add(m_exit);
+        menuBar.setBackground(new java.awt.Color(196,197,203));
+        menuBar.add(m_backendMenu);
+        menuBar.add(m_searchMenu);
+        menuBar.add(m_indices);
+        menuBar.add(m_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() ;
+                }
+            });
+        m_treePane.getViewport().add(m_tree);
+        m_tree.setBounds(new java.awt.Rectangle(6,184,82,80));
+        m_tree.setShowsRootHandles(true);
+        m_tree.setToolTipText("Jdbm DB DIT");
+        m_tree.setScrollsOnExpand(true) ;
+        m_tree.getSelectionModel().addTreeSelectionListener(
+            new TreeSelectionListener() {
+                public void valueChanged(TreeSelectionEvent e) {
+                    TreePath l_path = e.getNewLeadSelectionPath() ;
+
+                    if(l_path == null) {
+                        return ;
+                    }
+
+                    Object l_last = l_path.getLastPathComponent() ;
+                    try {
+                        if(l_last instanceof EntryNode) {
+                            displayEntry(((EntryNode) l_last).getLdapEntry()) ;
+                        }
+                    } catch(Exception ex) {
+                        ex.printStackTrace() ;
+                    }
+                }
+        }) ;
+
+        m_entryPane.getViewport().add(m_entryTbl);
+        m_entryTbl.setBounds(new java.awt.Rectangle(321,103,32,32));
+
+        m_idxPane.getViewport().add(m_idxTbl);
+        m_idxTbl.setBounds(new java.awt.Rectangle(429,134,32,32));
+
+        m_treePane.setSize(new java.awt.Dimension(285, 435));
+        m_treePane.setPreferredSize(new java.awt.Dimension(285, 403));
+        m_searchMenu.setText("Search") ;
+        m_searchMenu.setName("Search") ;
+        m_searchMenu.setBackground(new java.awt.Color(205,205,205));
+        m_searchMenu.add(m_run) ;
+        m_searchMenu.add(m_debug) ;
+        m_searchMenu.add(m_annotate) ;
+
+        ActionListener l_searchHandler = new ActionListener()
+        {
+            public void actionPerformed(ActionEvent an_event) {
+                System.out.println("action command = " + an_event.getActionCommand()) ;
+				doFilterDialog(an_event.getActionCommand()) ;
+            }
+        } ;
+
+        m_annotate.setText(FilterDialog.ANNOTATE_MODE) ;
+        m_annotate.setName(FilterDialog.ANNOTATE_MODE) ;
+        m_annotate.setActionCommand(FilterDialog.ANNOTATE_MODE) ;
+        m_annotate.setBackground(new java.awt.Color(205,205,205));
+        m_annotate.addActionListener(l_searchHandler) ;
+
+        m_run.setText(FilterDialog.RUN_MODE) ;
+        m_run.setName(FilterDialog.RUN_MODE) ;
+        m_run.setActionCommand(FilterDialog.RUN_MODE) ;
+        m_run.setBackground(new java.awt.Color(205,205,205));
+        m_run.addActionListener(l_searchHandler) ;
+
+        m_debug.setText(FilterDialog.DEBUG_MODE) ;
+        m_debug.setName(FilterDialog.DEBUG_MODE) ;
+        m_debug.setActionCommand(FilterDialog.DEBUG_MODE) ;
+        m_debug.setBackground(new java.awt.Color(205,205,205));
+        m_debug.addActionListener(l_searchHandler) ;
+
+        m_indices.setText("Indices") ;
+        m_indices.setName("Indices") ;
+        m_indices.setBackground(new java.awt.Color(205,205,205));
+    }
+
+
+    public void enableLogging(Logger a_logger)
+    {
+        m_logger = a_logger ;
+    }
+
+
+    public void launch()
+    {
+        //Center the frame on screen
+        Dimension l_screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+        Dimension l_frameSize = getSize() ;
+        l_frameSize.height = ((l_frameSize.height > l_screenSize.height)
+            ? l_screenSize.height : l_frameSize.height);
+        l_frameSize.width = ((l_frameSize.width > l_screenSize.width)
+            ? l_screenSize.width : l_frameSize.width);
+        setLocation((l_screenSize.width - l_frameSize.width) / 2,
+            (l_screenSize.height - l_frameSize.height) / 2);
+        setVisible(true) ;
+    }
+
+
+    /**
+     * Exit the Application
+     */
+    private void exitForm() {
+        this.setEnabled(false) ;
+        this.setVisible(false) ;
+        this.dispose() ;
+
+        if(m_doCleanUp && m_database != null) {
+            m_database.sync() ;
+            m_database.close() ;
+	        System.exit(0) ;
+        }
+    }
+
+
+    public void doFilterDialog(final String a_mode)
+    {
+        final FilterDialog l_dialog = new FilterDialog(a_mode, this, true) ;
+
+        if(this.m_tree.getSelectionModel().getSelectionPath() != null) {
+            TreePath l_path = m_tree.getSelectionModel().getSelectionPath() ;
+            Object l_last = l_path.getLastPathComponent() ;
+            String l_base = null ;
+            if(l_last instanceof EntryNode) {
+                LdapEntryImpl l_entry = ((EntryNode) l_last).getLdapEntry() ;
+                l_base = l_entry.getEntryDN() ;
+            } else {
+                l_base = m_database.getSuffix().toString() ;
+            }
+
+            l_dialog.setBase(l_base) ;
+        } else {
+            l_dialog.setBase(m_database.getSuffix().toString()) ;
+        }
+
+        l_dialog.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent an_event) {
+                String l_cmd = an_event.getActionCommand() ;
+
+                try {
+                    if(l_cmd.equals(FilterDialog.SEARCH_CMD)) {
+                        if(a_mode == FilterDialog.RUN_MODE) {
+                            doRun(l_dialog.getFilter(), l_dialog.getScope(),
+                                l_dialog.getBase(), l_dialog.getLimit()) ;
+                        } else if(a_mode == FilterDialog.DEBUG_MODE) {
+                            doDebug(l_dialog.getFilter(), l_dialog.getScope(),
+                                 l_dialog.getBase(), l_dialog.getLimit()) ;
+                        } else if(a_mode == FilterDialog.ANNOTATE_MODE) {
+                            if(doAnnotate(l_dialog.getFilter())) {
+                                // continue
+                            } else {
+                                // We failed don't loose users filter buf
+                                // allow user to make edits.
+                                return ;
+                            }
+                        } else {
+                            throw new RuntimeException("Unrecognized mode.") ;
+                        }
+                    } else if(l_cmd.equals(FilterDialog.CANCEL_CMD)) {
+                        // Do nothing! Just exit dialog.
+                    } else {
+                        throw new
+                            RuntimeException("Unrecognized FilterDialog command: "
+                                + l_cmd) ;
+                    }
+                } catch(Exception e) {
+                    e.printStackTrace() ;
+                }
+
+				l_dialog.setVisible(false) ;
+				l_dialog.dispose() ;
+            }
+        }) ;
+
+        //Center the frame on screen
+        l_dialog.setSize(456, 256) ;
+        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+        Dimension frameSize = l_dialog.getSize();
+        frameSize.height = ((frameSize.height > screenSize.height)
+            ? screenSize.height : frameSize.height);
+        frameSize.width = ((frameSize.width > screenSize.width)
+            ? screenSize.width : frameSize.width);
+        l_dialog.setLocation((screenSize.width - frameSize.width) / 2,
+            (screenSize.height - frameSize.height) / 2);
+        l_dialog.setEnabled(true) ;
+        l_dialog.setVisible(true) ;
+    }
+
+
+    public boolean doRun(String a_filter, String a_scope, String a_base,
+        String a_limit)
+        throws Exception
+    {
+        System.out.println("Search attempt using filter '" + a_filter + "' "
+            + "with scope '" + a_scope + "' and a return limit of '" + a_limit
+            + "'") ;
+        SearchEngine l_engine = new SearchEngine() ;
+        l_engine.enableLogging(m_logger) ;
+        FilterParser l_parser = new FilterParserImpl() ;
+        l_parser.setFilterParserMonitor( new FilterParserMonitorAdapter() ) ;
+        ExprNode l_root = null ;
+
+        try {
+            l_root = l_parser.parse(a_filter) ;
+        } catch(Exception e) {
+            JTextArea l_text = new JTextArea() ;
+            String l_msg = e.getMessage() ;
+
+            if(l_msg.length() > 1024) {
+                l_msg = l_msg.substring(0, 1024) + "\n. . . truncated . . ." ;
+            }
+
+            l_text.setText(l_msg) ;
+            l_text.setEnabled(false) ;
+            JOptionPane.showMessageDialog(null, l_text, "Syntax Error",
+                JOptionPane.ERROR_MESSAGE) ;
+            return false ;
+        }
+
+        int l_scope = -1 ;
+
+        if(a_scope == FilterDialog.BASE_SCOPE) {
+	        l_scope = Backend.BASE_SCOPE ;
+        } else if(a_scope == FilterDialog.SINGLE_SCOPE) {
+            l_scope = Backend.SINGLE_SCOPE ;
+        } else if(a_scope == FilterDialog.SUBTREE_SCOPE) {
+            l_scope = Backend.SUBTREE_SCOPE ;
+        } else {
+            throw new RuntimeException("Unexpected scope parameter: " +
+                a_scope) ;
+        }
+
+        int l_limit = Integer.MAX_VALUE ;
+        if(!a_limit.equals(FilterDialog.UNLIMITED)) {
+            l_limit = Integer.parseInt(a_limit) ;
+        }
+
+        Cursor l_cursor =
+            l_engine.search(m_database, l_root, a_base, l_scope) ;
+        String [] l_cols = new String [2] ;
+        l_cols[0] = "id" ;
+        l_cols[1] = "dn" ;
+        DefaultTableModel l_tableModel = new DefaultTableModel(l_cols, 0) ;
+        Object [] l_row = new Object[2] ;
+        int l_count = 0 ;
+        while(l_cursor.hasMore() && l_count < l_limit) {
+            IndexRecord l_rec = (IndexRecord) l_cursor.next() ;
+            l_row[0] = l_rec.getEntryId() ;
+            l_row[1] = m_database.getEntryDn((BigInteger) l_row[0]) ;
+            l_tableModel.addRow(l_row) ;
+            l_count++ ;
+        }
+
+        SearchResultDialog l_results = new SearchResultDialog(this, false) ;
+        StringBuffer l_buf = new StringBuffer() ;
+        l_buf.append("base: ").append(a_base).append('\n') ;
+        l_buf.append("scope: ").append(a_scope).append('\n') ;
+        l_buf.append("limit: ").append(a_limit).append('\n') ;
+        l_buf.append("total: ").append(l_count).append('\n') ;
+        l_buf.append("filter:\n").append(a_filter).append('\n') ;
+        l_results.setFilter(l_buf.toString()) ;
+
+	    TreeNode l_astRoot = new ASTNode(null, l_root) ;
+	    TreeModel l_treeModel = new DefaultTreeModel(l_astRoot, true) ;
+        l_results.setTreeModel(l_treeModel) ;
+        l_results.setTableModel(l_tableModel) ;
+        l_results.setVisible(true) ;
+        return true ;
+    }
+
+
+    public void doDebug(String a_filter, String a_scope, String a_base,
+        String a_limit)
+    {
+        System.out.println("Search attempt using filter '" + a_filter + "' "
+            + "with scope '" + a_scope + "' and a return limit of '" + a_limit
+            + "'") ;
+    }
+
+
+    public void selectTreeNode(BigInteger a_id)
+    {
+        Stack l_stack = new Stack() ;
+        TreeNode l_parent = (EntryNode) m_nodes.get(a_id) ;
+        while(l_parent != null && (l_parent != l_parent.getParent())) {
+            l_stack.push(l_parent) ;
+            l_parent = l_parent.getParent() ;
+        }
+
+
+        Object [] l_comps = null ;
+
+        if(l_stack.size() == 0) {
+            l_comps = new Object[1] ;
+            l_comps[0] = m_root ;
+        } else {
+            l_comps = new Object[l_stack.size()] ;
+        }
+
+        for(int ii = 0; l_stack.size() > 0 && ii < l_comps.length; ii++) {
+            l_comps[ii] = l_stack.pop() ;
+        }
+
+        TreePath l_path = new TreePath(l_comps) ;
+        m_tree.scrollPathToVisible(l_path) ;
+        m_tree.getSelectionModel().setSelectionPath(l_path) ;
+        m_tree.validate() ;
+    }
+
+
+    public boolean doAnnotate(String a_filter)
+        throws Exception
+    {
+		FilterParser l_parser = new FilterParserImpl() ;
+        l_parser.setFilterParserMonitor( new FilterParserMonitorAdapter() );
+        ExprNode l_root = null ;
+
+        try {
+            l_root = l_parser.parse(a_filter) ;
+        } catch(Exception e) {
+            JTextArea l_text = new JTextArea() ;
+            String l_msg = e.getMessage() ;
+
+            if(l_msg.length() > 1024) {
+                l_msg = l_msg.substring(0, 1024) + "\n. . . truncated . . ." ;
+            }
+
+            l_text.setText(l_msg) ;
+            l_text.setEnabled(false) ;
+            JOptionPane.showMessageDialog(null, l_text, "Syntax Error",
+                JOptionPane.ERROR_MESSAGE) ;
+            return false ;
+        }
+
+		AnnotatedFilterTreeDialog l_treeDialog = new
+			AnnotatedFilterTreeDialog(BackendFrame.this, false) ;
+		l_treeDialog.setFilter(a_filter) ;
+
+        DefaultOptimizer l_optimizer = new DefaultOptimizer() ;
+        l_optimizer.annotate(this.m_database, l_root) ;
+		TreeNode l_astRoot = new ASTNode(null, l_root) ;
+		TreeModel l_model = new DefaultTreeModel(l_astRoot, true) ;
+		l_treeDialog.setModel(l_model) ;
+        l_treeDialog.setVisible(true) ;
+        return true ;
+    }
+
+
+    public void showIndexDialog(String a_index)
+        throws Exception
+    {
+        System.out.println("Got request to show index dialog for " + a_index) ;
+        Index l_index = (Index) m_database.getIndex(a_index) ;
+
+        if(l_index != null) {
+            IndexDialog l_dialog = new IndexDialog(this, false, l_index) ;
+            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+            Dimension frameSize = l_dialog.getSize();
+            frameSize.height = ((frameSize.height > screenSize.height)
+                ? screenSize.height : frameSize.height);
+            frameSize.width = ((frameSize.width > screenSize.width)
+                ? screenSize.width : frameSize.width);
+            l_dialog.setLocation((screenSize.width - frameSize.width) / 2,
+                (screenSize.height - frameSize.height) / 2);
+            l_dialog.setEnabled(true) ;
+            l_dialog.setVisible(true) ;
+        }
+    }
+
+
+    public void buildIndicesMenu(Database a_database)
+    {
+        ActionListener l_listener = new ActionListener()
+        {
+            public void actionPerformed(ActionEvent a_event) {
+                try {
+                    showIndexDialog(a_event.getActionCommand()) ;
+                } catch(Exception e) {
+                    e.printStackTrace() ;
+                }
+            }
+        } ;
+
+        JMenuItem l_item = null ;
+        String [] l_sdi = Database.SYS_INDICES ;
+        Iterator l_udi = a_database.getUDIAttributes() ;
+
+        for(int ii = 0; ii < l_sdi.length; ii++) {
+			l_item = new JMenuItem() ;
+            l_item.setBackground(new java.awt.Color(205,205,205));
+            m_indices.add(l_item) ;
+            l_item.setText(l_sdi[ii]) ;
+            l_item.setName(l_sdi[ii]) ;
+            l_item.setActionCommand(l_sdi[ii]) ;
+            l_item.addActionListener(l_listener) ;
+        }
+
+        m_indices.add(new JSeparator()) ;
+
+        while(l_udi.hasNext()) {
+            String l_index = (String) l_udi.next() ;
+			l_item = new JMenuItem() ;
+            l_item.setBackground(new java.awt.Color(205,205,205));
+            m_indices.add(l_item) ;
+            l_item.setText(l_index) ;
+            l_item.setName(l_index) ;
+            l_item.setActionCommand(l_index) ;
+            l_item.addActionListener(l_listener) ;
+        }
+    }
+
+
+    void displayEntry(LdapEntryImpl a_entry)
+        throws Exception
+    {
+        MultiMapModel l_model = new MultiMapModel(a_entry) ;
+        this.m_entryTbl.setModel(l_model) ;
+
+        if(m_database != null) {
+            l_model = new MultiMapModel(
+                m_database.getIndices(a_entry.getEntryID())) ;
+            this.m_idxTbl.setModel(l_model) ;
+        } else {
+            this.m_idxTbl.setModel(null) ;
+        }
+
+        this.validate() ;
+    }
+
+
+    public void loadDatabase(Database a_database)
+        throws Exception
+    {
+        m_database = a_database ;
+        m_doCleanUp = false ;
+        load() ;
+    }
+
+
+    public void loadDatabase(String a_kernelClass, String a_dirPath)
+        throws Exception
+    {
+        m_doCleanUp = true ;
+
+        m_kernel = (Kernel) Class.forName(a_kernelClass).newInstance() ;
+        m_kernel.setRoot(a_dirPath) ;
+        m_kernel.bootStrap("backend0") ;
+        m_logger = m_kernel.getLogger() ;
+        JdbmModule l_backend = (JdbmModule)
+            m_kernel.getServiceManager().lookup(JdbmModule.ROLE) ;
+        m_database = l_backend.getDatabase() ;
+        load() ;
+    }
+
+
+    private void load()
+        throws Exception
+    {
+        boolean doFiltered = false ;
+        m_nodes = new HashMap() ;
+
+        int l_option =
+            JOptionPane.showConfirmDialog(null, "Would you like to filter "
+            + "leaf nodes on load?", "Use Filter?",
+            JOptionPane.OK_CANCEL_OPTION) ;
+        doFiltered = l_option == JOptionPane.OK_OPTION ;
+
+        if(doFiltered) {
+            SearchEngine l_engine = new SearchEngine() ;
+            final FilterDialog l_dialog =
+                new FilterDialog(FilterDialog.LOAD_MODE, this, true) ;
+            l_dialog.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    l_dialog.setVisible(false) ;
+                    l_dialog.dispose() ;
+                }
+            }) ;
+
+            l_dialog.setBase(m_database.getSuffix().toString()) ;
+            l_dialog.setScope(FilterDialog.SUBTREE_SCOPE) ;
+
+            //Center the frame on screen
+            l_dialog.setSize(456, 256) ;
+            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+            Dimension frameSize = l_dialog.getSize();
+            frameSize.height = ((frameSize.height > screenSize.height)
+                ? screenSize.height : frameSize.height);
+            frameSize.width = ((frameSize.width > screenSize.width)
+                ? screenSize.width : frameSize.width);
+            l_dialog.setLocation((screenSize.width - frameSize.width) / 2,
+                (screenSize.height - frameSize.height) / 2);
+            l_dialog.setEnabled(true) ;
+            l_dialog.setVisible(true) ;
+
+            FilterParser l_parser = new FilterParserImpl() ;
+            l_parser.setFilterParserMonitor( new FilterParserMonitorAdapter());
+            ExprNode l_exprNode = l_parser.parse(l_dialog.getFilter()) ;
+
+            int l_scope = -1 ;
+            String l_scopeStr = l_dialog.getScope() ;
+            if(l_scopeStr == FilterDialog.BASE_SCOPE) {
+                l_scope = Backend.BASE_SCOPE ;
+            } else if(l_scopeStr == FilterDialog.SINGLE_SCOPE) {
+                l_scope = Backend.SINGLE_SCOPE ;
+            } else if(l_scopeStr == FilterDialog.SUBTREE_SCOPE) {
+                l_scope = Backend.SUBTREE_SCOPE ;
+            } else {
+                throw new RuntimeException("Unrecognized scope") ;
+            }
+
+            l_exprNode =
+                l_engine.addScopeNode(l_exprNode, l_dialog.getBase(), l_scope) ;
+            m_root = new EntryNode(null, m_database,
+                m_database.getSuffixEntry(), m_nodes, l_exprNode, l_engine) ;
+        } else {
+            m_root = new EntryNode(null, m_database,
+                m_database.getSuffixEntry(), m_nodes) ;
+        }
+
+        DefaultTreeModel l_model = new DefaultTreeModel(m_root) ;
+        m_tree.setModel(l_model) ;
+        buildIndicesMenu(m_database) ;
+        if(this.isVisible()) {
+            m_tree.validate() ;
+        }
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/EntryNode.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/EntryNode.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,172 @@
+/*
+ * $Id: EntryNode.java,v 1.3 2003/03/13 18:27:22 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.gui ;
+
+
+import java.util.ArrayList ;
+import java.util.Enumeration ;
+import javax.swing.tree.TreeNode ;
+
+import org.apache.eve.backend.jdbm.Database ;
+import org.apache.eve.backend.jdbm.LdapEntryImpl ;
+import org.apache.eve.backend.jdbm.index.IndexRecord;
+import java.util.Collections;
+import org.apache.eve.backend.Cursor;
+import java.util.Iterator;
+import java.util.HashMap;
+import org.apache.ldap.common.filter.ExprNode ;
+import org.apache.eve.backend.jdbm.search.SearchEngine;
+
+
+/**
+ * A node representing an entry.
+ */
+public class EntryNode
+	implements TreeNode
+{
+    private final Database m_db ;
+    private final EntryNode m_parent ;
+	private final LdapEntryImpl m_entry ;
+    private final ArrayList m_children ;
+
+
+    public EntryNode(EntryNode a_parent, Database a_db, LdapEntryImpl a_entry,
+        HashMap a_map)
+    {
+        this(a_parent, a_db, a_entry, a_map, null, null) ;
+    }
+
+
+    public EntryNode(EntryNode a_parent, Database a_db, LdapEntryImpl a_entry,
+        HashMap a_map, ExprNode a_exprNode, SearchEngine a_engine)
+    {
+        m_db = a_db ;
+		m_entry = a_entry ;
+        m_children = new ArrayList() ;
+
+        if(a_parent == null) {
+            m_parent = this ;
+        } else {
+            m_parent = a_parent ;
+        }
+
+		try {
+            ArrayList l_records = new ArrayList() ;
+            Cursor l_cursor = m_db.getChildren(m_entry.getEntryID()) ;
+            while(l_cursor.hasMore()) {
+                IndexRecord l_old = (IndexRecord) l_cursor.next() ;
+                IndexRecord l_new = new IndexRecord() ;
+                l_new.setEntryId(l_old.getEntryId()) ;
+                l_new.setIndexKey(l_old.getIndexKey()) ;
+                l_records.add(l_new) ;
+            }
+            l_cursor.close() ;
+
+            Iterator l_list = l_records.iterator() ;
+            while(l_list.hasNext()) {
+                IndexRecord l_rec = (IndexRecord) l_list.next() ;
+
+                // Avoids root node which is both a parent and child of itself.
+                if(l_rec.getEntryId().equals(m_entry.getEntryID())) {
+                    continue ;
+                }
+
+                if(a_engine != null && a_exprNode != null) {
+                    if(m_db.getChildCount(l_rec.getEntryId()) == 0) {
+                        if(a_engine.assertExpression(m_db, a_exprNode,
+                            l_rec.getEntryId()))
+                        {
+                            LdapEntryImpl l_entry = m_db.read(l_rec.getEntryId()) ;
+                            EntryNode l_child =
+                                new EntryNode(this, m_db, l_entry, a_map,
+                                a_exprNode, a_engine) ;
+                            m_children.add(l_child) ;
+                        } else {
+                            continue ;
+                        }
+                    } else {
+                        LdapEntryImpl l_entry = m_db.read(l_rec.getEntryId()) ;
+                        EntryNode l_child = new EntryNode(this, m_db, l_entry,
+                            a_map, a_exprNode, a_engine) ;
+                        m_children.add(l_child) ;
+                    }
+                } else {
+                    LdapEntryImpl l_entry = m_db.read(l_rec.getEntryId()) ;
+                    EntryNode l_child =
+                        new EntryNode(this, m_db, l_entry, a_map) ;
+                    m_children.add(l_child) ;
+                }
+            }
+        } catch(Exception e) {
+            e.printStackTrace() ;
+        }
+
+        a_map.put(a_entry.getEntryID(), this) ;
+    }
+
+
+    public Enumeration children()
+    {
+        return Collections.enumeration(m_children) ;
+    }
+
+
+    public boolean getAllowsChildren()
+    {
+        return true ;
+    }
+
+
+    public TreeNode getChildAt(int a_childIndex)
+    {
+		return (TreeNode) m_children.get(a_childIndex) ;
+    }
+
+
+    public int getChildCount()
+    {
+        return m_children.size() ;
+    }
+
+
+    public int getIndex(TreeNode a_child)
+    {
+        return m_children.indexOf(a_child) ;
+    }
+
+
+    public TreeNode getParent()
+    {
+        return m_parent ;
+    }
+
+
+    public boolean isLeaf()
+    {
+        return m_children.size() <= 0 ;
+    }
+
+
+    public String toString()
+    {
+        StringBuffer l_buf = new StringBuffer() ;
+        l_buf.append(m_entry.getEntryDN()) ;
+        if(m_children.size() > 0) {
+            l_buf.append(" [").append(m_children.size()).append(']') ;
+        }
+        return l_buf.toString() ;
+    }
+
+
+    public LdapEntryImpl getLdapEntry()
+    {
+        return m_entry ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/FilterDialog.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/FilterDialog.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,273 @@
+/*
+ * $Id: FilterDialog.java,v 1.2 2003/03/13 18:27:23 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.gui ;
+
+
+import javax.swing.JLabel ;
+import javax.swing.JFrame ;
+import javax.swing.JPanel ;
+import javax.swing.JDialog ;
+import javax.swing.JButton ;
+import javax.swing.JComboBox ;
+import javax.swing.JTextArea ;
+import javax.swing.JTextField ;
+import javax.swing.BorderFactory ;
+import javax.swing.border.TitledBorder ;
+
+import java.awt.Font ;
+import java.awt.Color ;
+import java.awt.Insets ;
+import java.awt.Dimension ;
+import java.awt.FlowLayout ;
+import java.awt.BorderLayout ;
+import java.awt.GridBagLayout ;
+import java.awt.event.WindowEvent ;
+import java.awt.GridBagConstraints ;
+import java.awt.event.WindowAdapter ;
+import java.awt.event.ActionListener;
+
+import javax.swing.JScrollPane;
+
+
+public class FilterDialog
+    extends JDialog
+{
+	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(this.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() ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/IndexDialog.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/IndexDialog.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,256 @@
+/*
+ * $Id: IndexDialog.java,v 1.3 2003/03/13 18:27:24 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+package org.apache.eve.backend.jdbm.gui;
+
+import javax.swing.JDialog;
+import java.awt.Frame;
+import java.awt.Panel;
+import javax.swing.JTabbedPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JTextField;
+import javax.swing.JComboBox;
+import javax.swing.DefaultComboBoxModel;
+import org.apache.eve.backend.jdbm.index.Index;
+import org.apache.eve.backend.Cursor;
+import javax.swing.JOptionPane;
+import org.apache.regexp.RE;
+import org.apache.eve.backend.jdbm.index.IndexRecord;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import org.apache.avalon.framework.ExceptionUtil;
+import javax.swing.JTextArea;
+import org.apache.ldap.common.util.StringTools;
+
+public class IndexDialog extends JDialog
+{
+    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 m_mainPnl = new Panel();
+    private JTabbedPane m_tabbedPane = new JTabbedPane();
+    private JPanel m_listPnl = new JPanel();
+    private JPanel m_cursorPnl = new JPanel();
+    private JPanel m_resultsPnl = new JPanel();
+    private JScrollPane jScrollPane2 = new JScrollPane();
+    private JTable m_resultsTbl = new JTable();
+    private JPanel m_buttonPnl = new JPanel();
+    private JButton m_doneBut = new JButton();
+    private JLabel jLabel1 = new JLabel();
+    private JTextField m_keyText = new JTextField();
+    private JLabel jLabel2 = new JLabel();
+    private JComboBox m_cursorType = new JComboBox();
+    private JButton m_scanBut = new JButton();
+    private Index m_index = null ;
+
+    /** Creates new form JDialog */
+    public IndexDialog(Frame parent, boolean modal, Index a_index)
+    {
+        super(parent, modal) ;
+        m_index = a_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 '" + m_index.getAttribute() + "'") ;
+        setBounds(new java.awt.Rectangle(0, 0, 512, 471));
+        getContentPane().add(m_mainPnl, java.awt.BorderLayout.CENTER);
+        m_mainPnl.setLayout(new java.awt.BorderLayout());
+        m_mainPnl.add(m_tabbedPane, java.awt.BorderLayout.CENTER);
+        m_tabbedPane.add(m_listPnl, "Listing");
+        m_listPnl.setLayout(new java.awt.GridBagLayout());
+        m_listPnl.add(m_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));
+        m_listPnl.add(m_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));
+        m_listPnl.add(m_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));
+        m_cursorPnl.setLayout(new java.awt.GridBagLayout());
+        m_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)));
+        m_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));
+        m_cursorPnl.add(m_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));
+        m_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));
+        m_cursorPnl.add(m_cursorType,
+        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));
+        m_resultsPnl.setLayout(new java.awt.BorderLayout());
+        m_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)));
+        m_resultsPnl.add(jScrollPane2, java.awt.BorderLayout.CENTER);
+        jScrollPane2.getViewport().add(m_resultsTbl);
+        m_buttonPnl.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 15, 5));
+        m_buttonPnl.add(m_doneBut);
+        m_buttonPnl.add(m_scanBut);
+        m_doneBut.setText("Done");
+        m_doneBut.setName("Done");
+        m_doneBut.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                closeDialog() ;
+            }
+        }) ;
+
+        jLabel1.setText("Key Constraint:");
+        m_keyText.setText("");
+        m_keyText.setMinimumSize(new java.awt.Dimension(130, 20));
+        m_keyText.setPreferredSize(new java.awt.Dimension(130, 20));
+        m_keyText.setMaximumSize(new java.awt.Dimension(130, 20));
+        m_keyText.setFont(new java.awt.Font("SansSerif", java.awt.Font.PLAIN, 14));
+        m_keyText.setSize(new java.awt.Dimension(130, 20));
+        jLabel2.setText("Cursor Type:");
+        m_cursorType.setMaximumSize(new java.awt.Dimension(32767, 20));
+        m_cursorType.setMinimumSize(new java.awt.Dimension(126, 20));
+        m_cursorType.setPreferredSize(new java.awt.Dimension(130, 20));
+        DefaultComboBoxModel l_comboModel = new DefaultComboBoxModel() ;
+        l_comboModel.addElement(DEFAULT_CURSOR) ;
+        l_comboModel.addElement(EQUALITY_CURSOR) ;
+        l_comboModel.addElement(GREATER_CURSOR) ;
+        l_comboModel.addElement(LESS_CURSOR) ;
+        l_comboModel.addElement(REGEX_CURSOR) ;
+        m_cursorType.setModel(l_comboModel) ;
+        m_cursorType.setMaximumRowCount(5);
+        m_scanBut.setText("Scan");
+        m_scanBut.setName("Scan");
+        m_scanBut.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                doScan(m_keyText.getText(),
+                    (String) m_cursorType.getSelectedItem()) ;
+            }
+        }) ;
+
+        doScan(null, DEFAULT_CURSOR) ;
+    }
+
+
+    private void closeDialog()
+    {
+        setVisible(false) ;
+        dispose() ;
+    }
+
+
+    public boolean doScan(String a_key, String a_cursorType)
+    {
+        if(a_key == null || a_key.trim().equals("")) {
+            a_key = null ;
+        }
+
+        if(a_key == null && a_cursorType != DEFAULT_CURSOR) {
+            JOptionPane.showMessageDialog(null, "Cannot use a " +
+                a_cursorType + " cursor type with a null key constraint.",
+                "Missing Key Constraint", JOptionPane.ERROR_MESSAGE) ;
+            return false ;
+        }
+
+        try {
+            Cursor l_cursor = null ;
+
+            if(a_cursorType == EQUALITY_CURSOR) {
+                l_cursor = m_index.getCursor(a_key) ;
+            } else if(a_cursorType == GREATER_CURSOR) {
+                l_cursor = m_index.getCursor(a_key, true) ;
+            } else if(a_cursorType == LESS_CURSOR) {
+                l_cursor = m_index.getCursor(a_key, false) ;
+            } else if(a_cursorType == REGEX_CURSOR) {
+                RE l_regex = StringTools.getRegex(a_key) ;
+                int l_starIndex = a_key.indexOf('*') ;
+
+                if(l_starIndex > 0) {
+                    String l_prefix = a_key.substring(0, l_starIndex) ;
+                    System.out.println("Regex prefix = " + l_prefix) ;
+                    l_cursor = m_index.getCursor(l_regex, l_prefix) ;
+                } else {
+                    l_cursor = m_index.getCursor(l_regex) ;
+                }
+            } else {
+                l_cursor = m_index.getCursor() ;
+            }
+
+            Object [] l_cols = new Object [2] ;
+            Object [] l_row = null ;
+            l_cols[0] = "Keys (Attribute Value)" ;
+            l_cols[1] = "Values (Entry Id)" ;
+            DefaultTableModel l_model = new DefaultTableModel(l_cols, 0) ;
+            int l_count = 0 ;
+            while(l_cursor.hasMore()) {
+                IndexRecord l_rec = (IndexRecord) l_cursor.next() ;
+                l_row = new Object [2] ;
+                l_row[0] = l_rec.getIndexKey() ;
+                l_row[1] = l_rec.getEntryId() ;
+                l_model.addRow(l_row) ;
+                l_count++ ;
+            } ;
+
+            m_resultsTbl.setModel(l_model) ;
+            m_resultsPnl.setBorder(
+                javax.swing.BorderFactory.createTitledBorder(
+                javax.swing.BorderFactory.createLineBorder(
+                new java.awt.Color(153, 153, 153), 1),
+                "Scan Results: " + l_count,
+                javax.swing.border.TitledBorder.LEADING,
+                javax.swing.border.TitledBorder.TOP,
+                new java.awt.Font("SansSerif", 0, 14),
+                new java.awt.Color(60, 60, 60))) ;
+
+            if(this.isVisible()) {
+                this.validate() ;
+            }
+        } catch(Exception e) {
+            String l_msg = ExceptionUtil.printStackTrace(e) ;
+            if(l_msg.length() > 1024) {
+                l_msg = l_msg.substring(0, 1024)
+                    + "\n. . . TRUNCATED . . ." ;
+            }
+            l_msg = "Error while scanning index "
+                + "on attribute " + m_index.getAttribute() + " using a "
+                + a_cursorType + " cursor type with a key constraint of '"
+                + a_key + "':\n" + l_msg ;
+            JTextArea l_area = new JTextArea() ;
+            l_area.setText(l_msg) ;
+            JOptionPane.showMessageDialog(null, l_area, "Index Scan Error",
+                JOptionPane.ERROR_MESSAGE) ;
+            return false ;
+        }
+
+        return true ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/JdbmBackendViewer.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/JdbmBackendViewer.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,74 @@
+/*
+ * $Id: JdbmBackendViewer.java,v 1.2 2003/03/13 18:27:24 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.gui ;
+
+import javax.swing.UIManager ;
+import java.awt.Dimension ;
+import java.awt.Toolkit ;
+import java.io.File ;
+import javax.swing.JFileChooser ;
+
+import org.apache.log.Priority;
+import org.apache.log.Hierarchy;
+
+
+public class JdbmBackendViewer
+{
+    public JdbmBackendViewer()
+    {
+        BackendFrame frame = new BackendFrame() ;
+
+        String l_startPath = null ;
+        if(File.separatorChar == '/') {
+            l_startPath = "../projects" ;
+        } else {
+            l_startPath = "..\\projects" ;
+        }
+
+        final JFileChooser fc = new JFileChooser(l_startPath) ;
+        int returnVal = fc.showOpenDialog(frame);
+        if (returnVal == javax.swing.JFileChooser.APPROVE_OPTION) {
+            File file = fc.getSelectedFile() ;
+            try {
+                frame.loadDatabase("ldapd.test.Harness", file.getParent()) ;
+            } catch(Exception ex) {
+                ex.printStackTrace() ;
+            }
+        } else {
+            // Write your code here what to do if user has canceled Open dialog
+        }
+
+        //Center the frame on screen
+        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);
+        System.out.println(frameSize) ;
+    }
+
+
+    public static void main(String[] argv) {
+        // set up system Look&Feel
+        try {
+            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+        }
+        catch (Exception e) {
+            System.out.println("Could not set look and feel to use " +
+                UIManager.getSystemLookAndFeelClassName() + ".") ;
+            //e.printStackTrace() ;
+        }
+        new JdbmBackendViewer ();
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/JdbmTableModel.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/JdbmTableModel.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,73 @@
+/*
+ * $Id: JdbmTableModel.java,v 1.2 2003/03/13 18:27:24 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.gui ;
+
+
+import java.util.ArrayList ;
+import javax.swing.table.AbstractTableModel ;
+
+
+public class JdbmTableModel
+    extends AbstractTableModel
+{
+    public static final String KEY_COL = "Keys" ;
+    public static final String VAL_COL = "Values" ;
+
+    private ArrayList m_keyList ;
+    private ArrayList m_valList ;
+
+
+    public JdbmTableModel(ArrayList a_keyList, ArrayList a_valList)
+    {
+        this.m_keyList = a_keyList ;
+        this.m_valList = a_valList ;
+    }
+
+
+    public String getColumnName(int a_col)
+    {
+        if(a_col == 0) {
+            return KEY_COL ;
+        } else if(a_col == 1) {
+            return VAL_COL ;
+        } else {
+            throw new RuntimeException("There can only be 2 columns at index "
+                + "0 and at 1") ;
+        }
+    }
+
+
+    public int getRowCount()
+    {
+        return m_keyList.size() ;
+    }
+
+
+    public int getColumnCount()
+    {
+        return 2 ;
+    }
+
+
+    public Object getValueAt(int a_row, int a_col)
+    {
+		if(a_row >= m_keyList.size()) {
+			return("NULL") ;
+		}
+
+        if(this.getColumnName(a_col).equals(KEY_COL)) {
+            return this.m_keyList.get(a_row) ;
+        } else if(this.getColumnName(a_col).equals(VAL_COL)) {
+            return this.m_valList.get(a_row) ;
+        } else {
+            throw new RuntimeException("You did not correctly set col names") ;
+        }
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/MultiMapModel.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/MultiMapModel.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,91 @@
+/*
+ * $Id: MultiMapModel.java,v 1.2 2003/03/13 18:27:24 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.gui ;
+
+
+import java.util.ArrayList ;
+import javax.swing.table.AbstractTableModel ;
+import org.apache.commons.collections.MultiMap;
+import java.util.Iterator;
+import java.util.Collection;
+
+
+public class MultiMapModel
+    extends AbstractTableModel
+{
+    public static final String KEY_COL = "Keys" ;
+    public static final String VAL_COL = "Values" ;
+
+    private final MultiMap m_map ;
+    private final ArrayList m_keys ;
+    private final int m_rowCount ;
+    private final ArrayList m_vals ;
+
+
+    public MultiMapModel(MultiMap a_map)
+    {
+        this.m_map = a_map ;
+        m_rowCount = m_map.values().size() ;
+        this.m_keys = new ArrayList(m_rowCount) ;
+        this.m_vals = new ArrayList(m_rowCount) ;
+
+        Iterator l_keyList = m_map.keySet().iterator() ;
+        while(l_keyList.hasNext()) {
+            String l_key = (String) l_keyList.next() ;
+            Iterator l_valList = ((Collection) m_map.get(l_key)).iterator() ;
+            while(l_valList.hasNext()) {
+                String l_val = l_valList.next().toString() ;
+                m_keys.add(l_key) ;
+                m_vals.add(l_val) ;
+            }
+        }
+    }
+
+
+    public String getColumnName(int a_col)
+    {
+        if(a_col == 0) {
+            return KEY_COL ;
+        } else if(a_col == 1) {
+            return VAL_COL ;
+        } else {
+            throw new RuntimeException("There can only be 2 columns at index "
+                + "0 and at 1") ;
+        }
+    }
+
+
+    public int getRowCount()
+    {
+        return m_rowCount ;
+    }
+
+
+    public int getColumnCount()
+    {
+        return 2 ;
+    }
+
+
+    public Object getValueAt(int a_row, int a_col)
+    {
+		if(a_row >= m_rowCount) {
+			return("NULL") ;
+		}
+
+        if(this.getColumnName(a_col).equals(KEY_COL)) {
+            return this.m_keys.get(a_row) ;
+        } else if(this.getColumnName(a_col).equals(VAL_COL)) {
+            return this.m_vals.get(a_row) ;
+        } else {
+            throw new RuntimeException("You did not correctly set col names") ;
+        }
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/SearchResultDialog.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/SearchResultDialog.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,158 @@
+/*
+ * $Id: SearchResultDialog.java,v 1.2 2003/03/13 18:27:24 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.gui ;
+
+import javax.swing.JDialog ;
+import java.awt.Frame ;
+import java.awt.event.WindowEvent ;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.JTextArea;
+import javax.swing.JButton;
+import javax.swing.tree.TreeModel;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import javax.swing.JTable;
+import javax.swing.table.TableModel;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.ListSelectionModel;
+import java.math.BigInteger;
+
+
+public class SearchResultDialog
+    extends JDialog implements ListSelectionListener
+{
+    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 a_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 l_selectionModel = (ListSelectionModel) an_event.getSource() ;
+        int l_minIndex = l_selectionModel.getMinSelectionIndex() ;
+        int l_maxIndex = l_selectionModel.getMaxSelectionIndex() ;
+
+        for(int ii = l_minIndex ; ii <= l_maxIndex; ii++) {
+            if(l_selectionModel.isSelectedIndex(ii) && !an_event.getValueIsAdjusting()) {
+                BigInteger l_id = (BigInteger)
+                    m_resultsTbl.getModel().getValueAt(ii, 0) ;
+                ((BackendFrame) getParent()).selectTreeNode(l_id) ;
+            }
+        }
+    }
+
+
+    /** Closes the dialog */
+    private void closeDialog(WindowEvent evt) {
+        setVisible(false);
+        dispose();
+    }
+
+
+    public void setTreeModel(TreeModel a_model)
+    {
+        this.jTree1.setModel(a_model) ;
+    }
+
+
+    public void setFilter(String a_filter)
+    {
+        this.jTextArea1.setText(a_filter) ;
+    }
+
+
+    public void setTableModel(TableModel a_model)
+    {
+        m_resultsTbl.setModel(a_model) ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/gui/ldapd.gif
==============================================================================
Binary file. No diff available.

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/BigIntegerComparator.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/BigIntegerComparator.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,47 @@
+/*
+ * $Id: BigIntegerComparator.java,v 1.4 2003/03/13 18:27:25 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.index ;
+
+
+import java.math.BigInteger ;
+import org.apache.eve.backend.jdbm.table.TableComparator ;
+
+
+public class BigIntegerComparator extends TableComparator
+{
+    /**
+     * Version id for serialization.
+     */
+    final static long serialVersionUID = 1L ;
+
+
+    /**
+     * 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 an_obj1, Object an_obj2 ) {
+        if (an_obj1 == null) {
+            throw new IllegalArgumentException("Argument 'an_obj1' is null") ;
+        }
+
+        if (an_obj2 == null) {
+            throw new IllegalArgumentException("Argument 'an_obj2' is null") ;
+        }
+
+        //System.out.println("BigIntegerComparator.compare(" + an_obj1 + ", "
+        //    + an_obj2 + ")") ;
+        BigInteger l_int1 = (BigInteger) an_obj1 ;
+        BigInteger l_int2 = (BigInteger) an_obj2 ;
+        return l_int1.compareTo(l_int2) ;
+     }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/Index.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/Index.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,99 @@
+/*
+ * $Id: Index.java,v 1.6 2003/03/13 18:27:25 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.index ;
+
+import java.math.BigInteger ;
+import javax.naming.NamingException ;
+
+import org.apache.regexp.RE ;
+
+import org.apache.eve.backend.Cursor ;
+import org.apache.eve.backend.BackendException ;
+
+
+/**
+ * Doc me!
+ * @todo Doc me!
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.6 $
+ */
+public interface Index
+{
+    public String getAttribute() ;
+
+    public String getFilePath() ;
+
+    public int count()
+        throws BackendException, NamingException ;
+
+    public int count(Object an_attrVal)
+        throws BackendException, NamingException ;
+
+    public int count(Object an_attrVal, boolean isGreaterThan)
+        throws BackendException, NamingException ;
+
+    public BigInteger getForward(Object an_attrVal)
+        throws BackendException, NamingException ;
+
+    public Object getReverse(BigInteger a_id)
+        throws BackendException ;
+
+	public void add(Object an_attrVal, BigInteger a_id)
+        throws BackendException, NamingException ;
+
+	public void drop(Object an_attrVal, BigInteger a_id)
+        throws BackendException, NamingException ;
+
+
+    ///////////////////////
+    // Cursor Operations //
+    ///////////////////////
+
+    /*
+    public IndexCursor getReverseCursor()
+        throws BackendException, NamingException ;
+        */
+
+    public IndexCursor getReverseCursor(BigInteger a_id)
+        throws BackendException, NamingException ;
+
+    public IndexCursor getCursor()
+        throws BackendException, NamingException ;
+
+    public IndexCursor getCursor(Object an_attrVal)
+        throws BackendException, NamingException ;
+
+    public IndexCursor getCursor(Object an_attrVal, boolean isGreaterThan)
+        throws BackendException, NamingException ;
+
+    public IndexCursor getCursor(RE a_regex)
+        throws BackendException, NamingException ;
+
+    public IndexCursor getCursor(RE a_regex, String a_prefix)
+        throws BackendException, NamingException ;
+
+    public boolean hasValue(Object an_attrVal, BigInteger a_id)
+        throws BackendException, NamingException ;
+
+    public boolean hasValue(Object an_attrVal, BigInteger a_id,
+        boolean isGreaterThan)
+        throws BackendException, NamingException ;
+
+    public boolean hasValue(RE a_regex, BigInteger a_id)
+        throws BackendException, NamingException ;
+
+    public void close()
+        throws BackendException ;
+
+    public void sync()
+        throws BackendException ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/IndexComparator.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/IndexComparator.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,110 @@
+/*
+ * $Id: IndexComparator.java,v 1.4 2003/03/13 18:27:26 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.index ;
+
+
+import org.apache.eve.backend.jdbm.table.TupleComparator ;
+import org.apache.eve.backend.jdbm.table.TableComparator ;
+
+
+/**
+ * Doc me!
+ * @todo Doc me!
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.4 $
+ */
+public class IndexComparator
+    implements TupleComparator
+{
+    public static final StringComparator strComp =
+        new StringComparator() ;
+    public static final BigIntegerComparator intComp =
+        new BigIntegerComparator() ;
+
+    private final boolean isForwardMap ;
+    private final TableComparator m_keyComp ;
+
+
+    public IndexComparator(TableComparator a_keyComp, boolean isForwardMap)
+    {
+        m_keyComp = a_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 TableComparator getKeyComparator()
+    {
+        if(this.isForwardMap) {
+            return m_keyComp ;
+        }
+
+        return intComp ;
+    }
+
+
+    /**
+     * 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 TableComparator getValueComparator()
+    {
+        if(this.isForwardMap) {
+            return intComp ;
+        }
+
+        return m_keyComp ;
+    }
+
+
+    /**
+     * Compares key Object to determine their sorting order returning a
+     * value = to, < or > than 0.
+     *
+     * @param a_key1 the first key to compare
+     * @param a_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 a_key1, Object a_key2)
+    {
+        jdbm.helper.Comparator l_comparator =
+            (jdbm.helper.Comparator) getKeyComparator() ;
+        return l_comparator.compare(a_key1, a_key2) ;
+    }
+
+
+    /**
+     * Comparse value Objects to determine their sorting order returning a
+     * value = to, < or > than 0.
+     *
+     * @param a_value1 the first value to compare
+     * @param a_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 a_value1, Object a_value2)
+    {
+        jdbm.helper.Comparator l_comparator =
+            (jdbm.helper.Comparator) getValueComparator() ;
+        return l_comparator.compare(a_value1, a_value2) ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/IndexCursor.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/IndexCursor.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,106 @@
+/*
+ * $Id: IndexCursor.java,v 1.3 2003/03/13 18:27:26 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.index ;
+
+
+import org.apache.eve.backend.Cursor ;
+import org.apache.eve.backend.BackendException;
+import javax.naming.NamingException;
+import jdbm.helper.Tuple;
+import org.apache.regexp.RE;
+
+
+public class IndexCursor
+    extends Cursor
+{
+    private final RE m_re ;
+    private final IndexRecord m_tmp = new IndexRecord() ;
+    private final IndexRecord m_returned = new IndexRecord() ;
+    private final IndexRecord m_prefetched = new IndexRecord() ;
+    private final boolean swapKeyVal ;
+    private final Cursor m_cursor ;
+
+
+    IndexCursor(Cursor a_cursor)
+        throws BackendException, NamingException
+    {
+        this(a_cursor, false, null) ;
+    }
+
+
+    IndexCursor(Cursor a_cursor, boolean swapKeyVal)
+        throws BackendException, NamingException
+    {
+        this(a_cursor, swapKeyVal, null) ;
+    }
+
+
+    IndexCursor(Cursor a_cursor, boolean swapKeyVal, RE a_regex)
+        throws BackendException, NamingException
+    {
+        m_re = a_regex ;
+        m_cursor = a_cursor ;
+        this.swapKeyVal = swapKeyVal ;
+
+        if(a_cursor.isClosed()) {
+            super.close() ;
+            return ;
+        }
+
+        prefetch() ;
+    }
+
+
+    private void prefetch()
+        throws BackendException, NamingException
+    {
+        while(m_cursor.hasMore()) {
+            Tuple l_tuple = (Tuple) m_cursor.next() ;
+
+            if(swapKeyVal) {
+                m_tmp.setSwapped(l_tuple) ;
+            } else {
+                m_tmp.setTuple(l_tuple) ;
+            }
+
+            // 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 == m_re || m_re.match((String) m_tmp.getIndexKey())) {
+                m_prefetched.setIndexKey(m_tmp.getIndexKey()) ;
+                m_prefetched.setEntryId(m_tmp.getEntryId()) ;
+                return ;
+            }
+        }
+
+        // If we got down here then m_cursor has been consumed without a match!
+        close() ;
+    }
+
+
+    protected Object advance()
+        throws BackendException, NamingException
+    {
+        m_returned.setEntryId(m_prefetched.getEntryId()) ;
+        m_returned.setIndexKey(m_prefetched.getIndexKey()) ;
+        prefetch() ;
+        return m_returned ;
+    }
+
+
+    protected boolean canAdvance()
+        throws BackendException, NamingException
+    {
+        return !isClosed() ;
+    }
+
+
+    protected void freeResources() {/*Does nothing*/}
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/IndexRecord.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/IndexRecord.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,59 @@
+/*
+ * $Id: IndexRecord.java,v 1.3 2003/03/13 18:27:26 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.index;
+
+
+import jdbm.helper.Tuple ;
+import java.math.BigInteger ;
+
+
+public class IndexRecord
+{
+    private final Tuple m_tuple = new Tuple() ;
+
+
+    public void setTuple(Tuple a_tuple)
+    {
+        m_tuple.setKey(a_tuple.getKey()) ;
+        m_tuple.setValue(a_tuple.getValue()) ;
+    }
+
+
+    public void setSwapped(Tuple a_tuple)
+    {
+        m_tuple.setKey(a_tuple.getValue()) ;
+        m_tuple.setValue(a_tuple.getKey()) ;
+    }
+
+
+    public BigInteger getEntryId()
+    {
+        return (BigInteger) m_tuple.getValue() ;
+    }
+
+
+    public Object getIndexKey()
+    {
+        return m_tuple.getKey() ;
+    }
+
+
+    public void setEntryId(BigInteger a_id)
+    {
+        m_tuple.setValue(a_id) ;
+    }
+
+
+    public void setIndexKey(Object a_key)
+    {
+        m_tuple.setKey(a_key) ;
+    }
+}
+

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/JdbmIndex.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/JdbmIndex.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,400 @@
+/*
+ * Id: JdbmIndex.java,v 1.10 2003/01/31 04:26:27 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.index ;
+
+
+import java.io.IOException ;
+import java.io.File ;
+import java.math.BigInteger ;
+import javax.naming.NameParser ;
+import javax.naming.NamingException ;
+
+import org.apache.eve.schema.Schema ;
+import org.apache.eve.backend.Cursor ;
+import org.apache.ldap.common.util.StringTools ;
+import org.apache.eve.schema.Normalizer ;
+import org.apache.ldap.common.NotImplementedException ;
+import org.apache.eve.backend.BackendException ;
+import org.apache.eve.backend.jdbm.table.JdbmTable ;
+import org.apache.eve.backend.jdbm.table.TableComparator ;
+
+import jdbm.recman.RecordManager ;
+
+import org.apache.regexp.RE ;
+import org.apache.commons.collections.LRUMap ;
+import org.apache.avalon.framework.logger.Logger ;
+import org.apache.avalon.framework.ExceptionUtil ;
+import org.apache.avalon.framework.logger.AbstractLogEnabled ;
+
+
+/**
+ * Doc me!
+ * @todo Doc me!
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.12 $
+ */
+public class JdbmIndex
+    extends AbstractLogEnabled
+    implements Index
+{
+    public static final String FORWARD_BTREE = "_forward" ;
+    public static final String REVERSE_BTREE = "_reverse" ;
+
+    private final boolean isString ;
+    private final boolean isDecimal ;
+    private final boolean isInteger ;
+    private final String m_filePath ;
+    private final String m_attribute ;
+    private final Normalizer m_normalizer ;
+
+    private JdbmTable m_forward = null ;
+    private JdbmTable m_reverse = null ;
+    private RecordManager m_recMan = null ;
+    private LRUMap m_keyCache = null ;
+
+
+    public JdbmIndex(String a_attribute, Schema a_schema, String a_wkDirPath)
+        throws BackendException, NamingException
+    {
+        File l_file = new File(a_wkDirPath + File.separator + a_attribute) ;
+        m_filePath = l_file.getAbsolutePath() ;
+        m_attribute = a_attribute ;
+
+        TableComparator l_tableComp = null ;
+
+        if(a_attribute == Schema.DN_ATTR ||
+            a_attribute.equals(Schema.DN_ATTR)) {
+            final NameParser l_parser = a_schema.getNormalizingParser() ;
+            m_normalizer = new Normalizer() {
+                public String getEqualityMatch() {
+                    return "distinguishedNameMatch" ;
+                }
+                public String normalize(String a_str)
+                    throws NamingException
+                {
+                    return l_parser.parse(a_str).toString() ;
+                }
+            } ;
+            this.isDecimal = false ;
+            this.isString = true ;
+            this.isInteger = false ;
+            m_keyCache = new LRUMap(1000) ;
+            l_tableComp = IndexComparator.strComp ;
+        } else if(a_attribute == Schema.EXISTANCE_ATTR ||
+            a_attribute.equals(Schema.EXISTANCE_ATTR)) {
+            m_normalizer = new Normalizer() {
+                public String getEqualityMatch() {
+                    return "caseIgnoreMatch" ;
+                }
+                public String normalize(String a_str) {
+                    return StringTools.deepTrimToLower(a_str) ;
+                }
+            } ;
+            this.isDecimal = false ;
+            this.isString = true ;
+            this.isInteger = false ;
+            m_keyCache = new LRUMap(1000) ;
+            l_tableComp = IndexComparator.strComp ;
+        } else if(a_attribute == Schema.HIERARCHY_ATTR ||
+            a_attribute.equals(Schema.HIERARCHY_ATTR)) {
+            m_normalizer = new Normalizer() {
+                public String getEqualityMatch() {
+                    return "integerMatch" ;
+                }
+                public String normalize(String a_str) {
+                    return a_str ;
+                }
+            } ;
+            this.isDecimal = false ;
+            this.isString = false ;
+            this.isInteger = true ;
+            l_tableComp = IndexComparator.intComp ;
+        } else {
+            m_normalizer = a_schema.getNormalizer(m_attribute, true) ;
+            if(a_schema.isDecimal(m_attribute)) {
+                this.isDecimal = true ;
+                this.isInteger = false ;
+                this.isString = false ;
+    
+                // We do not have a plan for handling decimal values now!
+                throw new NotImplementedException() ;
+            } else if(a_schema.isNumeric(m_attribute)) {
+                this.isInteger = true ;
+                this.isDecimal = false ;
+                this.isString = false ;
+                l_tableComp = IndexComparator.intComp ;
+            } else if(a_schema.isBinary(m_attribute)) {
+                throw new IllegalArgumentException(
+                    "Cannot create an index on binary attribute: '"
+                    + m_attribute + "'") ;
+            } else {
+                this.isInteger = false ;
+                this.isDecimal = false ;
+                this.isString = true ;
+                l_tableComp = IndexComparator.strComp ;
+                m_keyCache = new LRUMap(1000) ;
+            }
+        }
+
+        try {
+            m_recMan = new RecordManager(m_filePath) ;
+            m_recMan.disableTransactions() ;
+        } catch(IOException e) {
+            throw new BackendException("Could not initialize the record "
+                + "manager:\n" + ExceptionUtil.printStackTrace(e), e) ;
+        }
+
+        m_forward = new JdbmTable(m_attribute + FORWARD_BTREE,
+            true, m_recMan, new IndexComparator(l_tableComp, true)) ;
+        m_reverse = new JdbmTable(m_attribute + REVERSE_BTREE,
+            true, m_recMan, new IndexComparator(l_tableComp, false)) ;
+    }
+
+
+    public void enableLogging(Logger a_logger)
+    {
+        super.enableLogging(a_logger) ;
+        m_forward.enableLogging(a_logger) ;
+        m_reverse.enableLogging(a_logger) ;
+    }
+
+
+    public String getAttribute()
+    {
+        return m_attribute ;
+    }
+
+
+    public String getFilePath()
+    {
+        return m_filePath ;
+    }
+
+
+    ////////////////////////
+    // Scan Count Methods //
+    ////////////////////////
+
+
+    public int count()
+        throws BackendException, NamingException
+    {
+        return m_forward.count() ;
+    }
+
+
+    public Object getCanonical(Object an_attrVal)
+        throws NamingException
+    {
+        if(this.isString) {
+            String l_canonical = (String) m_keyCache.get(an_attrVal) ;
+
+            if(null == l_canonical) {
+                l_canonical = m_normalizer.normalize((String) an_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.
+                m_keyCache.put(an_attrVal, l_canonical) ;
+                m_keyCache.put(l_canonical, l_canonical) ;
+            }
+
+            return l_canonical ;
+        }
+
+        return an_attrVal ;
+    }
+
+
+    public int count(Object an_attrVal)
+        throws BackendException, NamingException
+    {
+        return m_forward.count(getCanonical(an_attrVal)) ;
+    }
+
+
+    public int count(Object an_attrVal, boolean isGreaterThan)
+        throws BackendException, NamingException
+    {
+        return m_forward.count(getCanonical(an_attrVal), isGreaterThan) ;
+    }
+
+
+    /////////////////////////////////
+    // Forward and Reverse Lookups //
+    /////////////////////////////////
+
+
+    public BigInteger getForward(Object an_attrVal)
+        throws BackendException, NamingException
+    {
+        return (BigInteger) m_forward.get(getCanonical(an_attrVal)) ;
+    }
+
+
+    public Object getReverse(BigInteger a_id)
+        throws BackendException
+    {
+        return m_reverse.get(a_id) ;
+    }
+
+
+    //////////////////////
+    // Add/Drop Methods //
+    //////////////////////
+
+
+    public synchronized void add(Object an_attrVal, BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        m_forward.put(getCanonical(an_attrVal), a_id) ;
+        m_reverse.put(a_id, getCanonical(an_attrVal)) ;
+    }
+
+
+    public synchronized void drop(Object an_attrVal, BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        m_forward.remove(getCanonical(an_attrVal), a_id) ;
+        m_reverse.remove(a_id, getCanonical(an_attrVal)) ;
+    }
+
+
+    ///////////////////////
+    // Cursor Operations //
+    ///////////////////////
+
+
+    public IndexCursor getReverseCursor(BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        IndexCursor l_cursor =
+            new IndexCursor(m_reverse.getCursor(a_id), true) ;
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    public IndexCursor getCursor()
+        throws BackendException, NamingException
+    {
+        IndexCursor l_cursor = new IndexCursor(m_forward.getCursor()) ;
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    public IndexCursor getCursor(Object an_attrVal)
+        throws BackendException, NamingException
+    {
+        IndexCursor l_cursor =
+            new IndexCursor(m_forward.getCursor(getCanonical(an_attrVal))) ;
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    public IndexCursor getCursor(Object an_attrVal, boolean isGreaterThan)
+        throws BackendException, NamingException
+    {
+        IndexCursor l_cursor = new IndexCursor(
+            m_forward.getCursor(getCanonical(an_attrVal), isGreaterThan)) ;
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    public IndexCursor getCursor(RE a_regex)
+        throws BackendException, NamingException
+    {
+        IndexCursor l_cursor =
+            new IndexCursor(m_forward.getCursor(), false, a_regex) ;
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    public IndexCursor getCursor(RE a_regex, String a_prefix)
+        throws BackendException, NamingException
+    {
+        IndexCursor l_cursor = new IndexCursor(m_forward.
+            getCursor(getCanonical(a_prefix), true), false, a_regex) ;
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    //////////////////////////////////////////////////
+    // Value Assertion (a.k.a Index Lookup) Methods //
+    //////////////////////////////////////////////////
+
+
+    public boolean hasValue(Object an_attrVal, BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        return m_forward.has(getCanonical(an_attrVal), a_id) ;
+    }
+
+
+    public boolean hasValue(Object an_attrVal, BigInteger a_id,
+        boolean isGreaterThan)
+        throws BackendException, NamingException
+    {
+        return m_forward.has(getCanonical(an_attrVal), a_id, isGreaterThan) ;
+    }
+
+
+    public boolean hasValue(RE a_regex, BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        IndexCursor l_cursor =
+            new IndexCursor(m_reverse.getCursor(a_id), true, a_regex) ;
+        l_cursor.enableLogging(getLogger()) ;
+
+        boolean hasValue = l_cursor.hasMore() ;
+        l_cursor.close() ;
+        return hasValue ;
+    }
+
+
+    /////////////////////////
+    // Maintenance Methods //
+    /////////////////////////
+
+
+    public synchronized void close()
+        throws BackendException
+    {
+        try {
+            m_forward.close() ;
+			m_reverse.close() ;
+            m_recMan.commit() ;
+            m_recMan.close() ;
+        } catch(IOException e) {
+            throw new BackendException("Encountered error while closing "
+                + "backend index file for attribute " + this.m_attribute) ;
+        }
+    }
+
+
+    public synchronized void sync()
+        throws BackendException
+    {
+        try {
+            m_recMan.commit() ;
+        } catch(IOException e) {
+            throw new BackendException("Encountered error while closing "
+                + "backend index file for attribute " + this.m_attribute) ;
+        }
+    }
+}
+

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/StringComparator.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/index/StringComparator.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,46 @@
+/*
+ * $Id: StringComparator.java,v 1.2 2003/03/13 18:27:27 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.index ;
+
+
+import org.apache.eve.backend.jdbm.table.TableComparator ;
+
+
+/**
+ * A StringComparator that is both a java.util.Comparator and a
+ * jdbm.helper.Comparator.
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.2 $
+ */
+public class StringComparator extends TableComparator
+{
+    /**
+     * Version id for serialization.
+     */
+    final static long serialVersionUID = 1L ;
+
+
+    /**
+     * Compare two objects.
+     */
+     public int compare( Object a_obj1, Object a_obj2 ) {
+        if (null == a_obj1) {
+            throw new IllegalArgumentException("Argument 'a_obj1' is null") ;
+        }
+
+        if (null == a_obj2) {
+            throw new IllegalArgumentException("Argument 'a_obj2' is null") ;
+        }
+
+        return ((String) a_obj1).compareTo(a_obj2) ;
+     }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/Assertion.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/Assertion.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,28 @@
+/*
+ * $Id: Assertion.java,v 1.2 2003/03/13 18:27:28 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.search ;
+
+
+import javax.naming.NamingException ;
+import org.apache.eve.backend.BackendException ;
+
+
+public interface Assertion
+{
+    /**
+     * Checks to see if a candidate is valid by evaluating an assertion
+     * against the candidate.
+     * 
+     * @param a_candidate the candidate to assert
+     * @return true if the candidate is valid, false otherwise
+     */
+    boolean assertCandidate(Object a_candidate)
+        throws BackendException, NamingException ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/DefaultOptimizer.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/DefaultOptimizer.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,199 @@
+/*
+ * $Id: DefaultOptimizer.java,v 1.2 2003/03/13 18:27:28 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.search ;
+
+
+import java.util.ArrayList ;
+import java.math.BigInteger ;
+import javax.naming.NamingException ;
+
+import org.apache.eve.schema.Schema ;
+import org.apache.eve.backend.Backend ;
+import org.apache.eve.backend.jdbm.Database ;
+import org.apache.eve.backend.BackendException ;
+
+import org.apache.ldap.common.filter.ExprNode ;
+import org.apache.ldap.common.filter.LeafNode ;
+import org.apache.ldap.common.filter.ScopeNode ;
+import org.apache.ldap.common.filter.SimpleNode ;
+import org.apache.ldap.common.filter.BranchNode ;
+import org.apache.ldap.common.filter.PresenceNode ;
+
+
+/**
+ *
+ */
+public class DefaultOptimizer
+    implements Optimizer
+{
+    public static final BigInteger MAX = BigInteger.valueOf(Integer.MAX_VALUE) ;
+
+    /**
+     * 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.
+     */
+    public void annotate(Database a_db, ExprNode a_node)
+        throws BackendException, NamingException
+    {
+        // Start off with the worst case unless scan count say otherwise.
+        BigInteger l_count = MAX ;
+
+        if ( a_node instanceof ScopeNode )
+        {
+            ScopeNode l_scopeNode = (ScopeNode) a_node ;
+
+            if(l_scopeNode.getScope() == Backend.BASE_SCOPE) {
+                l_count = BigInteger.ONE ;
+            } else if(l_scopeNode.getScope() == Backend.SINGLE_SCOPE) {
+                BigInteger l_id = a_db.getEntryId(l_scopeNode.getBaseDn()) ;
+                l_count = BigInteger.valueOf(a_db.getChildCount(l_id)) ;
+            } else if(l_scopeNode.getScope() == Backend.SUBTREE_SCOPE) {
+                l_count = BigInteger.valueOf(a_db.count()) ;
+            }
+        }
+
+            //////////////////////////////////////////////////////////
+            //           H A N D L E   L E A F   N O D E S          //
+            //////////////////////////////////////////////////////////
+
+        // Each assertion leaf node is on an attribute.  We ask the index on
+        // that attribute for the scan count representing all the candidates
+        // that would satisfy the assertion.
+        else if(a_node.isLeaf()) {
+            LeafNode l_leaf = (LeafNode) a_node ;
+            switch(l_leaf.getAssertionType()) {
+            case(LeafNode.APPROXIMATE):
+                l_count = BigInteger.valueOf(a_db.getIndexScanCount(
+                    l_leaf.getAttribute(),
+                    ((SimpleNode) l_leaf).getValue())) ;
+                break ;
+            case(LeafNode.EQUALITY):
+                l_count = BigInteger.valueOf(a_db.getIndexScanCount(
+                    l_leaf.getAttribute(),
+                    ((SimpleNode) l_leaf).getValue())) ;
+                break ;
+            case(LeafNode.EXTENSIBLE):
+                l_count = BigInteger.valueOf(a_db.getIndexScanCount(
+                    l_leaf.getAttribute())) ;
+                break ;
+            case(LeafNode.GREATEREQ):
+                l_count = BigInteger.valueOf(a_db.getIndexScanCount(
+                    l_leaf.getAttribute(),
+                    ((SimpleNode) l_leaf).getValue(), true)) ;
+                break ;
+            case(LeafNode.LESSEQ):
+                l_count = BigInteger.valueOf(a_db.getIndexScanCount(
+                    l_leaf.getAttribute(),
+                    ((SimpleNode) l_leaf).getValue(), false)) ;
+                break ;
+            case(LeafNode.PRESENCE):
+                PresenceNode l_presenceNode = (PresenceNode) a_node ;
+                l_count = BigInteger.valueOf(a_db.getIndexScanCount(
+                    Schema.EXISTANCE_ATTR,
+                    l_presenceNode.getAttribute())) ;
+                break ;
+            case(LeafNode.SUBSTRING):
+                l_count = BigInteger.valueOf(a_db.getIndexScanCount(
+                    l_leaf.getAttribute())) ;
+                break ;
+            default:
+                throw new IllegalArgumentException("Unrecognized leaf node") ;
+            }
+
+            //////////////////////////////////////////////////////////
+            //         H A N D L E   B R A N C H   N O D E S        //
+            //////////////////////////////////////////////////////////
+
+        } else {
+            ArrayList l_children = null ;
+            BranchNode l_node = (BranchNode) a_node ;
+
+            switch(l_node.getOperator()) {
+            case(BranchNode.AND):
+                //
+                // 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.
+                //
+                l_count = BigInteger.valueOf(Integer.MAX_VALUE) ;
+                l_children = l_node.getChildren() ;
+                for(int ii = 0; ii < l_children.size(); ii++) {
+                    ExprNode l_child = (ExprNode) l_children.get(ii) ;
+                    annotate(a_db, l_child) ;
+                    l_count =((BigInteger) l_child.get("count")).min(l_count) ;
+                }
+                break ;
+            case(BranchNode.NOT):
+                //
+                // Negation counts are estimated in two ways.  First if the
+                // sole child of the negation is a leaf or simple assertion
+                // on an existing index then the count of the negation is set
+                // to the count of all records in the respective index table
+                // as a very rough estimate of the worst possible case.
+                //
+                // If the child is a brach node then the count of the negation
+                // node is set to the total count of entries in the master
+                // table. This tactic is used to get a rough estimate since it
+                // is very costly to estimate the count of a negation over
+                // a compound expression.
+                //
+                ExprNode l_onlyChild = (ExprNode) l_node.getChildren().get(0) ;
+                annotate(a_db, l_onlyChild) ;
+
+                if(l_onlyChild.isLeaf()) {
+                    LeafNode l_leaf = (LeafNode) l_onlyChild ;
+                    String l_attribute = l_leaf.getAttribute() ;
+
+                    if(l_leaf instanceof PresenceNode) {
+                        l_count = BigInteger.valueOf(a_db.getIndexScanCount(
+                            Schema.EXISTANCE_ATTR)) ;
+                    } else {
+                        l_count = BigInteger.valueOf(a_db.getIndexScanCount(
+                            l_attribute)) ;
+                    }
+                } else {
+                    l_count = BigInteger.valueOf(a_db.count()) ;
+                }
+
+                break ;
+            case(BranchNode.OR):
+                //
+                // Disjunctions (OR) are the union of candidates across all
+                // subexpressions for this reason we accumulate the counts of
+                // all the child nodes. Notice that we annotate the child node
+                // with a recursive call before accessing its count parameter.
+                //
+                l_children = l_node.getChildren() ;
+                BigInteger l_total = BigInteger.ZERO ;
+                for(int ii = 0; ii < l_children.size(); ii++) {
+                    ExprNode l_child = (ExprNode) l_children.get(ii) ;
+                    annotate(a_db, l_child) ;
+                    l_total = l_total.add((BigInteger) l_child.get("count")) ;
+                }
+
+                l_count = l_total ;
+                break ;
+            default:
+                throw new IllegalArgumentException("Unrecognized branch node") ;
+            }
+        }
+
+        // Protect against overflow when counting.
+        if(l_count.compareTo(BigInteger.ZERO) < 0) {
+            l_count = MAX ;
+        }
+
+        a_node.set("count", l_count) ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/DisjunctionCursor.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/DisjunctionCursor.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,195 @@
+/*
+ * $Id: DisjunctionCursor.java,v 1.5 2003/05/04 17:46:52 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.search ;
+
+
+import java.util.Map ;
+import java.util.HashMap ;
+import javax.naming.NamingException ;
+
+import org.apache.eve.backend.Cursor ;
+import org.apache.eve.backend.AtomicBackend ;
+import org.apache.eve.backend.BackendModule ;
+import org.apache.eve.protocol.ProtocolModule ;
+import org.apache.eve.backend.BackendException ;
+import org.apache.eve.backend.jdbm.index.IndexRecord;
+
+
+/**
+ * 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:aok123@bellsouth.net"> Alex Karasulu </a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.5 $
+ */
+public class DisjunctionCursor
+    extends Cursor
+{
+    /** The underlying child cursors */
+    private final Cursor [] m_children ;
+    /** LUT used to avoid returning duplicates */
+    private final Map m_candidates = new HashMap() ;
+    /** Index of current cursor used */
+    private int m_index = 0 ;
+    /** Candidate to return */
+    private final IndexRecord m_candidate = new IndexRecord() ;
+    /** Prefetched record returned */
+    private final IndexRecord m_prefetched = new IndexRecord() ;
+
+
+    /**
+     * Creates a DisjunctionCursor over a set of child Cursors.  The returned
+     * result is the union of all underlying Cursors without duplicates.
+     *
+     * @param a_backend the owning backend instance
+     * @param a_children array of child Cursors
+     */
+    public DisjunctionCursor(Cursor [] a_children)
+        throws NamingException
+    {
+        m_children = a_children ;
+
+        // Close this cursor if their are no children.
+        if(a_children.length <= 0) {
+            close() ;
+            return ;
+        }
+
+
+        // Advance to the first cursor that has a candidate for us.
+        while(!m_children[m_index].hasMore()) {
+            m_index++ ;
+
+            // Close and return if we exhaust the cursors without finding a
+            // valid candidate to return.
+            if(m_index >= m_children.length) {
+                close() ;
+                return ;
+            }
+        }
+
+        // Grab the next candidate and add it's id to the LUT/hash of candidates
+        IndexRecord l_rec = (IndexRecord) m_children[m_index].next() ;
+        m_prefetched.setEntryId(l_rec.getEntryId()) ;
+        m_prefetched.setIndexKey(l_rec.getIndexKey()) ;
+        m_candidates.put(m_prefetched.getEntryId(), m_prefetched.getEntryId()) ;
+    }
+
+
+    /**
+     * 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
+     */
+    public Object advance()
+        throws BackendException, NamingException
+    {
+        // Store the last prefetched candidate to return in m_candidate
+        m_candidate.setEntryId(m_prefetched.getEntryId()) ;
+        m_candidate.setIndexKey(m_prefetched.getIndexKey()) ;
+
+        do {
+            // Advance to a Cursor that has the next valid candidate for us.
+            while(!m_children[m_index].hasMore()) {
+				if(getLogger().isDebugEnabled()) {
+					getLogger().debug("" //ProtocolModule.getMessageKey()
+						+ " - DisjunctionCursor.advance(): child cursor "
+						+ m_children[m_index] + " at index "
+						+ m_index + " has been exhausted.") ;
+				}
+
+                m_index++ ;
+        
+                // Close and return existing prefetched candidate if we
+                // have exhausted the underlying Cursors without finding a
+                // valid candidate to return.
+                if(m_index >= m_children.length) {
+					if(getLogger().isDebugEnabled()) {
+						getLogger().debug("" //ProtocolModule.getMessageKey()
+							+ " - DisjunctionCursor.advance(): no more child "
+							+ "indices left they have all been exhausted. "
+							+ "Closing this cursor. Returning last prefetched "
+                            + "candidate value of " + m_candidate) ;
+					}
+
+                    close() ;
+                    return m_candidate ;
+                }
+            }
+
+            // Grab next candidate!
+            IndexRecord l_rec = (IndexRecord) m_children[m_index].next() ;
+            m_prefetched.setIndexKey(l_rec.getIndexKey()) ;
+            m_prefetched.setEntryId(l_rec.getEntryId()) ;
+
+			if(getLogger().isDebugEnabled()) {
+				getLogger().debug("" //ProtocolModule.getMessageKey()
+					+ " - DisjunctionCursor.advance(): got next prefetched "
+					+ "candidate value of " + m_prefetched.getEntryId()
+					+ " Did we see it before? "
+                    + m_candidates.containsKey(m_prefetched.getEntryId())) ;
+			}
+
+            // Break through do/while if the candidate is seen for the first
+            // time, meaning we have not returned it already.
+        } while(m_candidates.containsKey(m_prefetched.getEntryId())) ;
+
+		if(getLogger().isDebugEnabled()) {
+			getLogger().debug("" //ProtocolModule.getMessageKey()
+				+ " - DisjunctionCursor.advance(): got next valid candidate "
+				+ "with a value of " + m_prefetched.getEntryId()
+                + ". Returning last "
+                + "prefetched value of " + m_candidate.getEntryId()) ;
+		}
+
+        // Add candidate to LUT of encountered candidates.
+        m_candidates.put(m_candidate.getEntryId(), m_candidate.getEntryId()) ;
+
+        // Return the original prefetched value
+        return m_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 canAdvance()
+        throws BackendException, NamingException
+    {
+        return !isClosed() ;
+    }
+
+
+    /**
+     * Closes all the underlying Cursors.
+     */
+    public void freeResources()
+    {
+        for(int ii = 0; ii < m_children.length; ii++) {
+            // Close all children but don't fail fast meaning don't stop
+            // closing all children if one fails to close for some reason.
+            try {
+                m_children[ii].close() ;
+            } catch(Throwable t) {
+                getLogger().warn("Could not close underlying cursor " +
+                    m_children[ii], t) ;
+            }
+        }
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/Optimizer.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/Optimizer.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,32 @@
+/*
+ * $Id: Optimizer.java,v 1.2 2003/03/13 18:27:29 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.search ;
+
+
+import javax.naming.NamingException ;
+
+import org.apache.ldap.common.filter.ExprNode ;
+import org.apache.eve.backend.jdbm.Database ;
+import org.apache.eve.backend.BackendException ;
+
+
+/**
+ * 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.
+ */
+public interface Optimizer
+{
+    public void annotate(Database a_db, ExprNode a_node)
+        throws BackendException, NamingException ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/PrefetchCursor.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/PrefetchCursor.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,185 @@
+/*
+ * $Id: PrefetchCursor.java,v 1.3 2003/03/13 18:27:29 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.search ;
+
+
+import java.util.Map ;
+import java.util.HashMap ;
+import java.util.ArrayList ;
+
+import java.math.BigInteger ;
+import javax.naming.NamingException ;
+
+import org.apache.eve.backend.Cursor ;
+import org.apache.ldap.common.filter.ExprNode ;
+import org.apache.eve.backend.jdbm.JdbmDatabase ;
+import org.apache.eve.protocol.ProtocolModule ;
+import org.apache.eve.backend.BackendException ;
+import org.apache.eve.backend.jdbm.search.Assertion ;
+import org.apache.eve.backend.jdbm.index.IndexRecord ;
+
+
+/**
+ * A LogicalCursor represents a Cursor over wither a AND/NOT/OR expression in a
+ * filter. This cursor prefetches underlying Cursor values so that it can comply
+ * with the defined Cursor semantics.
+ * 
+ * @author <a href="mailto:aok123@bellsouth.net"> Alex Karasulu </a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.3 $
+ */
+public class PrefetchCursor
+    extends Cursor
+{
+    /** The prefetched candidate */
+    private final IndexRecord m_prefetched = new IndexRecord() ;
+    /** The returned candidate */
+    private final IndexRecord m_candidate = new IndexRecord() ;
+    /** The iteration cursor */
+    private final Cursor m_cursor ;
+    /** LUT used to avoid returning duplicates */
+    private final Map m_candidates ;
+    private final Assertion m_assertion ;
+    private final boolean checkDups ;
+
+
+    /**
+     * Cursor over a conjunction expression.  All children except the candidate
+     * child to be used for iteration are provided as expressions. The child
+     * cursor is the basis for the iteration.
+     *
+     * @param a_backend the owning backend instance
+     * @param a_cursor underlying iteration cursor
+     * @param a_assertions array of assertions minus the assertion expression
+     * used to construct this cursor.
+     */
+    public PrefetchCursor(Cursor a_cursor, Assertion a_assertion)
+        throws BackendException, NamingException
+    {
+        m_cursor = a_cursor ;
+        m_candidates = null ;
+        m_assertion = a_assertion ;
+        checkDups = false ;
+        prefetch() ;
+    }
+
+
+    public PrefetchCursor(Cursor a_cursor,
+        Assertion a_assertion, boolean enableDupCheck)
+        throws BackendException, NamingException
+    {
+        m_cursor = a_cursor ;
+        m_candidates = new HashMap() ;
+        m_assertion = a_assertion ;
+        checkDups = true ;
+        prefetch() ;
+    }
+
+
+    /**
+     * Checks to see is a candidate is valid by evaluation all assertions
+     * against the candidate.  Any assertion failure short circuts tests
+     * returning false.  Success through all assertions returns true.
+     *
+     * @param a_candidate the candidate to assert
+     * @return true if the candidate is valid, false otherwise
+     * @throws ClassCastException if the candidate is not a BigInteger.
+     */
+    protected boolean assertCandidate(Object a_candidate)
+        throws BackendException, NamingException
+    {
+        return m_assertion.assertCandidate(a_candidate) ;
+    }
+
+
+    /**
+     * Advances this Cursor one position.  Underlying Cursor may be advanced
+     * several possitions while trying to find the next prefetched candidate to
+     * return.  If underlying Cursor elements are not valid meaning they do not
+     * pass assertions then they are rejected for return and the next item is
+     * tested.  If the underlying Cursor is consumed, then the last prefetched
+     * value is returned and this Cursor is closed.
+     *
+     * @return a valid candidate element that passed all assertions.
+     */
+    public Object advance()
+        throws BackendException, NamingException
+    {
+        m_candidate.setEntryId(m_prefetched.getEntryId()) ;
+        m_candidate.setIndexKey(m_prefetched.getIndexKey()) ;
+        prefetch() ;
+        return m_candidate ;
+    }
+
+
+    private void prefetch()
+        throws BackendException, NamingException
+    {
+        IndexRecord l_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(m_cursor.hasMore()) {
+            l_rec = (IndexRecord) m_cursor.next() ;
+
+            // If value is valid then we set it as the next candidate to return
+            if(assertCandidate(l_rec)) {
+                // dup checking is on but candidate is not in already seen LUT
+                // so we need to set it as next to return and add it to the LUT
+                if(checkDups && !m_candidates.containsKey(l_rec.getEntryId())) {
+                    m_prefetched.setEntryId(l_rec.getEntryId()) ;
+                    m_prefetched.setIndexKey(l_rec.getIndexKey()) ;
+                    m_candidates.put(l_rec.getEntryId(), l_rec.getEntryId()) ;
+                    return ;
+                // dup checking is on and candidate has already been seen so we
+                // need to skip it.
+                } else if(checkDups &&
+                    m_candidates.containsKey(l_rec.getEntryId()))
+                {
+                    continue ;
+                }
+
+                m_prefetched.setEntryId(l_rec.getEntryId()) ;
+                m_prefetched.setIndexKey(l_rec.getIndexKey()) ;
+                return ;
+            }
+        }
+
+        // At this pt the underlying Cursor has been exhaused so we close up
+        // and set the prefetched value to null so canAdvance returns false.
+        close() ;
+    }
+
+
+    /**
+     * 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 canAdvance()
+        throws BackendException, NamingException
+    {
+        return !isClosed() ;
+    }
+
+
+    /**
+     * Closes the underlying Cursor.
+     */
+    public void freeResources()
+    {
+        try {
+            m_cursor.close() ;
+        } catch(NamingException e) {
+            getLogger().warn("Could not close conjunction cursor child", e) ;
+        }
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/SearchEngine.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/search/SearchEngine.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,493 @@
+/*
+ * $Id: SearchEngine.java,v 1.5.4.1 2003/10/01 03:37:55 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.search ;
+
+
+import java.util.Iterator ;
+import java.util.ArrayList ;
+import java.math.BigInteger ;
+import javax.naming.NamingException ;
+
+
+import org.apache.eve.schema.Schema ;
+import org.apache.eve.backend.Cursor ;
+import org.apache.eve.backend.Backend ;
+import org.apache.ldap.common.filter.ExprNode ;
+import org.apache.ldap.common.filter.LeafNode ;
+import org.apache.ldap.common.filter.ScopeNode ;
+import org.apache.ldap.common.filter.SimpleNode ;
+import org.apache.ldap.common.filter.BranchNode ;
+import org.apache.ldap.common.filter.PresenceNode ;
+import org.apache.ldap.common.filter.SubstringNode ;
+import org.apache.eve.backend.jdbm.Database ;
+import org.apache.ldap.common.NotImplementedException ;
+import org.apache.ldap.common.message.DerefAliasesEnum;
+import org.apache.eve.backend.BackendException ;
+import org.apache.eve.backend.jdbm.index.Index ;
+import org.apache.eve.backend.jdbm.index.IndexRecord ;
+
+
+import org.apache.avalon.framework.logger.AbstractLogEnabled ;
+
+import org.apache.regexp.RE ;
+import org.apache.regexp.RESyntaxException ;
+
+
+/**
+ * Given a search filter and a scope the search engine identifies valid
+ * candidate entries returning their ids.
+ */
+public class SearchEngine
+    extends AbstractLogEnabled
+{
+    private Optimizer m_optimizer = new DefaultOptimizer() ;
+
+
+	public Optimizer getOptimizer()
+    {
+        return m_optimizer ;
+    }
+
+
+    public ExprNode addScopeNode(ExprNode a_node, String a_baseDn, int a_scope)
+    {
+        ScopeNode l_scopeNode = new ScopeNode(
+            DerefAliasesEnum.NEVERDEREFALIASES, a_baseDn, a_scope) ;
+        BranchNode l_top = new BranchNode(BranchNode.AND) ;
+        l_top.getChildren().add(l_scopeNode) ;
+        l_top.getChildren().add(a_node) ;
+        return l_top ;
+    }
+
+
+    public Cursor search(Database a_db,
+        ExprNode a_filter,
+        String a_baseDn,
+        int a_scope)
+        throws BackendException, NamingException
+    {
+        Cursor l_cursor = null ;
+        ExprNode l_root = addScopeNode(a_filter, a_baseDn, a_scope) ;
+        m_optimizer.annotate(a_db, l_root) ;
+        l_cursor = buildSearchCursor(a_db, l_root) ;
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    ///////////////////////////////////////////////////////////////
+    //     C U R S O R   B U I L D I N G   F U N C T I O N S     //
+    ///////////////////////////////////////////////////////////////
+
+
+    public Cursor buildSearchCursor(final Database a_db, final ExprNode a_node)
+        throws BackendException, NamingException
+    {
+        Cursor l_cursor = null ;
+
+        if ( a_node instanceof ScopeNode )
+        {
+            return buildScopeCursor( a_db, ( ScopeNode ) a_node ) ;
+        }
+
+        if(a_node.isLeaf()) {
+            LeafNode l_leaf = (LeafNode) a_node ;
+
+            switch(l_leaf.getAssertionType()) {
+            case(LeafNode.APPROXIMATE):
+                l_cursor = a_db.getIndexCursor(l_leaf.getAttribute(),
+                    ((SimpleNode)l_leaf).getValue()) ;
+                break ;
+            case(LeafNode.EQUALITY):
+                l_cursor = a_db.getIndexCursor(l_leaf.getAttribute(),
+                    ((SimpleNode)l_leaf).getValue()) ;
+                break ;
+            case(LeafNode.EXTENSIBLE):
+                // N O T   I M P L E M E N T E D   Y E T !
+                throw new NotImplementedException() ;
+                //break ;
+            case(LeafNode.GREATEREQ):
+                l_cursor = a_db.getIndexCursor(l_leaf.getAttribute(),
+                    ((SimpleNode)l_leaf).getValue(), true) ;
+                break ;
+            case(LeafNode.LESSEQ):
+                l_cursor = a_db.getIndexCursor(l_leaf.getAttribute(),
+                    ((SimpleNode)l_leaf).getValue(), false) ;
+                break ;
+            case(LeafNode.PRESENCE):
+                l_cursor = a_db.getIndexCursor(Schema.EXISTANCE_ATTR,
+                    l_leaf.getAttribute().toLowerCase()) ;
+                break ;
+            case(LeafNode.SUBSTRING):
+                l_cursor = buildSubstringCursor(a_db, (SubstringNode) l_leaf) ;
+                break ;
+            default:
+                throw new IllegalArgumentException("Unknown leaf assertion") ;
+            }
+        }
+        else if (a_node instanceof ScopeNode )
+        {
+            l_cursor = buildScopeCursor(a_db, (ScopeNode) a_node) ;
+        }
+        else
+        {
+            BranchNode l_branch = (BranchNode) a_node ;
+            switch(l_branch.getOperator()) {
+            case(BranchNode.AND):
+                l_cursor = buildConjunctionCursor(a_db, l_branch) ;
+                break ;
+            case(BranchNode.NOT):
+                l_cursor = buildNegationCursor(a_db, l_branch) ;
+                break ;
+            case(BranchNode.OR):
+                l_cursor = buildDisjunctionCursor(a_db, l_branch) ;
+                break ;
+            default:
+                throw new IllegalArgumentException("Unknown branch operator") ;
+            }
+        }
+
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    public Cursor buildScopeCursor(final Database a_db, final ScopeNode a_node)
+        throws BackendException, NamingException
+	{
+        switch(a_node.getScope()) {
+        case(Backend.BASE_SCOPE):
+            final BigInteger l_id = a_db.getEntryId(a_node.getBaseDn()) ;
+            final IndexRecord l_record =
+                new IndexRecord() ;
+            l_record.setEntryId(l_id) ;
+            l_record.setIndexKey(a_node.getBaseDn()) ;
+
+            return new Cursor() {
+                public Object advance()
+                    throws NamingException
+                { super.close() ; return l_record ; }
+                public void freeResources() { }
+                public boolean canAdvance()
+                { return(!super.isClosed()) ; }
+            } ;
+        case(Backend.SINGLE_SCOPE):
+            return a_db.getChildren(a_db.getEntryId(a_node.getBaseDn())) ;
+        case(Backend.SUBTREE_SCOPE):
+            Assertion l_assertion = new Assertion()
+            {
+                public boolean assertCandidate(Object a_candidate)
+                    throws BackendException
+                {
+                    IndexRecord l_rec = (IndexRecord) a_candidate ;
+                    String l_dn = a_db.getEntryDn(l_rec.getEntryId()) ;
+                    return l_dn.endsWith(a_node.getBaseDn()) ;
+                }
+            } ;
+
+            // Gets a cursor over all elements
+            Cursor l_cursor = a_db.getIndexCursor(Schema.DN_ATTR) ;
+            return new PrefetchCursor(l_cursor, l_assertion) ;
+        default:
+            throw new BackendException("Unrecognized search scope!") ;
+        }
+    }
+
+
+    public Cursor buildSubstringCursor(final Database a_db,
+        final SubstringNode a_node)
+        throws BackendException, NamingException
+	{
+        Cursor l_cursor = null ;
+        RE l_regex = null ;
+
+        try {
+            l_regex = a_node.getRegex() ;
+        } catch(RESyntaxException e) {
+            throw new BackendException("SubstringNode '" + a_node + "' had "
+                + "encountered syntax exception: " + e.getMessage(), e) ;
+        }
+
+        if(a_node.getInitial() != null) {
+	        l_cursor = a_db.getIndexCursor(a_node.getAttribute(), l_regex,
+                a_node.getInitial()) ;
+	    } else {
+            l_cursor = a_db.getIndexCursor(a_node.getAttribute(), l_regex) ;
+        }
+
+        return l_cursor ;
+    }
+
+
+    /**
+     * Method involved in chain recursion while constructing the search cursor
+     * used to handle the Disjunction expression case.
+     */
+    public Cursor buildDisjunctionCursor(final Database a_db,
+        final BranchNode a_node)
+        throws BackendException, NamingException
+    {
+        Cursor l_cursor = null ;
+        ArrayList l_list = a_node.getChildren() ;
+        Cursor [] l_childCursors = new Cursor [l_list.size()] ;
+
+        // Recursively create Cursors for each of the child expression nodes.
+        for(int ii = 0 ; ii < l_childCursors.length; ii++) {
+            l_childCursors[ii] =
+                buildSearchCursor(a_db, (ExprNode) l_list.get(ii)) ;
+        }
+
+        // Create the Cursor enable logging on it and return.
+        l_cursor = new DisjunctionCursor(l_childCursors) ;
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    /**
+     * Method involved in chain recursion while constructing the search cursor
+     * used to handle the Negation expression case.
+     */
+    public Cursor buildNegationCursor(final Database a_db,
+        final BranchNode a_node)
+        throws BackendException, NamingException
+    {
+        Cursor l_childCursor = null ;
+        Cursor l_cursor = null ;
+        final ExprNode l_childNode =
+            (ExprNode) a_node.getChildren().get(0) ;
+
+        // Iterates over entire set of index values.
+        if(l_childNode.isLeaf()) {
+            LeafNode l_child = (LeafNode) l_childNode ;
+            l_childCursor = a_db.getIndexCursor(l_child.getAttribute()) ;
+        } else { // Iterates over the entire set of entries.
+            l_childCursor = a_db.getIndexCursor(Schema.DN_ATTR) ;
+        }
+
+        l_childCursor.enableLogging(getLogger()) ;
+
+        Assertion l_assertion = new Assertion()
+        {
+            public boolean assertCandidate(Object a_candidate)
+                throws BackendException, NamingException
+            {
+                IndexRecord l_rec = (IndexRecord) a_candidate ;
+                // NOTICE THE ! HERE
+                // The candidate is valid if it does not pass assertion. A
+                // candidate that passes assertion is therefore invalid.
+                return !assertExpression(a_db, l_childNode, l_rec.getEntryId()) ;
+            }
+        } ;
+        l_cursor = new PrefetchCursor(l_childCursor, l_assertion, true) ;
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    /**
+     * Method involved in chain recursion while constructing the search cursor
+     * used to handle the Conjunction expression case.
+     */
+    public Cursor buildConjunctionCursor(final Database a_db,
+        final BranchNode a_node)
+        throws BackendException, NamingException
+    {
+        int l_minIndex = 0 ;
+        int l_minValue = Integer.MAX_VALUE ;
+        int l_value = Integer.MAX_VALUE ;
+        ExprNode l_node = null ;
+        Cursor l_cursor = null ;
+
+        // 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 cursor over its expression.
+        final ArrayList l_list = a_node.getChildren() ;
+        for(int ii = 0 ; ii < l_list.size(); ii++) {
+            l_node = (ExprNode) l_list.get(ii) ;
+            l_value = ((BigInteger) l_node.get("count")).intValue() ;
+            l_minValue = Math.min(l_minValue, l_value) ;
+
+            if(l_minValue == l_value) {
+                l_minIndex = ii ;
+            }
+        }
+
+        // Once found we construct the child cursor and the conjunction cursor.
+        final ExprNode l_cursorNode = (ExprNode) l_list.get(l_minIndex) ;
+
+        Assertion l_assertion = new Assertion()
+        {
+            public boolean assertCandidate(Object a_candidate)
+                throws BackendException, NamingException
+            {
+                IndexRecord l_rec = (IndexRecord) a_candidate ;
+
+                for(int ii = 0 ; ii < l_list.size(); ii++) {
+                    ExprNode l_child = (ExprNode) l_list.get(ii) ;
+
+                    if(l_child == l_cursorNode) {
+                        continue ;
+                    } else if(!assertExpression(a_db, l_child,
+                        l_rec.getEntryId())) {
+                        return false ;
+                    }
+                }
+
+                return true ;
+            }
+        } ;
+
+        Cursor l_childCursor = buildSearchCursor(a_db, l_cursorNode) ;
+        l_cursor = new PrefetchCursor(l_childCursor, l_assertion) ;
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    /////////////////////////////////////////////////////////////////////
+    //   E X P R E S S I O N   A S S E R T I O N   F U N C T I O N S   //
+    /////////////////////////////////////////////////////////////////////
+
+
+    public boolean assertExpression(final Database a_db, final ExprNode a_node,
+        final BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        if (a_node instanceof ScopeNode)
+        {
+            return assertScope( a_db, (ScopeNode) a_node, a_id ) ;
+        }
+        if(a_node.isLeaf()) {
+            return assertLeaf(a_db, (LeafNode) a_node, a_id) ;
+        }
+
+        return assertBranch(a_db, (BranchNode) a_node, a_id) ;
+    }
+
+
+    public boolean assertBranch(final Database a_db, final BranchNode a_node,
+        final BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        switch(a_node.getOperator()) {
+        case(BranchNode.OR):
+            Iterator l_children = a_node.getChildren().iterator() ;
+            while(l_children.hasNext()) {
+                ExprNode l_child = (ExprNode) l_children.next() ;
+                if(assertExpression(a_db, l_child, a_id)) {
+                    return true ;
+                }
+            }
+
+            return false ;
+        case(BranchNode.AND):
+            l_children = a_node.getChildren().iterator() ;
+            while(l_children.hasNext()) {
+                ExprNode l_child = (ExprNode) l_children.next() ;
+                if(!assertExpression(a_db, l_child, a_id)) {
+                    return false ;
+                }
+            }
+
+            return true ;
+        case(BranchNode.NOT):
+            ArrayList l_childArray = a_node.getChildren() ;
+            if(l_childArray.size() > 0) {
+                return !assertExpression(a_db,
+                    (ExprNode) l_childArray.get(0), a_id) ;
+            }
+
+            throw new BackendException("Negation has no child: " + a_node) ;
+        default:
+            throw new BackendException("Unrecognized branch node operator: "
+                + a_node.getOperator()) ;
+        }
+    }
+
+
+    public boolean assertLeaf(final Database a_db, final LeafNode a_node,
+        final BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        switch(a_node.getAssertionType()) {
+        case(LeafNode.APPROXIMATE):
+            return a_db.assertIndexValue(((SimpleNode) a_node).getAttribute(),
+                ((SimpleNode) a_node).getValue(), a_id) ;
+        case(LeafNode.EQUALITY):
+            return a_db.assertIndexValue(((SimpleNode) a_node).getAttribute(),
+                ((SimpleNode) a_node).getValue(), a_id) ;
+        case(LeafNode.EXTENSIBLE):
+            throw new NotImplementedException() ;
+        case(LeafNode.GREATEREQ):
+            return a_db.assertIndexValue(((SimpleNode) a_node).getAttribute(),
+                ((SimpleNode) a_node).getValue(), a_id, true) ;
+        case(LeafNode.LESSEQ):
+            return a_db.assertIndexValue(((SimpleNode) a_node).getAttribute(),
+                ((SimpleNode) a_node).getValue(), a_id, false) ;
+        case(LeafNode.PRESENCE):
+            return a_db.assertIndexValue(Schema.EXISTANCE_ATTR,
+                ((PresenceNode) a_node).getAttribute().toLowerCase(), a_id) ;
+        case(LeafNode.SUBSTRING):
+            return assertSubstring(a_db, (SubstringNode) a_node, a_id) ;
+        default:
+            throw new BackendException("Unrecognized leaf node type: "
+                + a_node.getAssertionType()) ;
+        }
+    }
+
+
+    public boolean assertScope(final Database a_db, final ScopeNode a_node,
+        final BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        String l_dn = a_db.getEntryDn(a_id) ;
+
+        switch(a_node.getScope()) {
+        case(Backend.BASE_SCOPE):
+            return l_dn.equals(a_node.getBaseDn()) ;
+        case(Backend.SINGLE_SCOPE):
+            Object l_key = a_db.getEntryId(a_node.getBaseDn()) ;
+            return a_db.assertIndexValue(Schema.HIERARCHY_ATTR, l_key, a_id) ;
+        case(Backend.SUBTREE_SCOPE):
+            return l_dn.endsWith(a_node.getBaseDn()) ;
+        default:
+            throw new BackendException("Unrecognized search scope!") ;
+        }
+    }
+
+
+    public boolean assertSubstring(final Database a_db,
+        final SubstringNode a_node,
+        final BigInteger a_id)
+        throws BackendException, NamingException
+    {
+        RE l_regex = null ;
+        Index l_index = a_db.getIndex(a_node.getAttribute()) ;
+        Cursor l_cursor = l_index.getReverseCursor(a_id) ;
+
+        try {
+            l_regex = a_node.getRegex() ;
+        } catch(RESyntaxException e) {
+            throw new BackendException("SubstringNode '" + a_node + "' had "
+                + "encountered syntax exception: " + e.getMessage(), e) ;
+        }
+
+        while(l_cursor.hasMore()) {
+            IndexRecord l_rec = (IndexRecord) l_cursor.next() ;
+            if(l_regex.match((String) l_rec.getIndexKey())) {
+                l_cursor.close() ;
+                return true ;
+            }
+        }
+
+        return false ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/DupsCursor.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/DupsCursor.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,111 @@
+/*
+ * $Id: DupsCursor.java,v 1.3 2003/03/13 18:27:30 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.table ;
+
+
+import java.util.TreeSet ;
+import java.util.Iterator ;
+import java.util.ArrayList ;
+import java.util.Collections ;
+
+import javax.naming.NamingException ;
+
+import org.apache.eve.backend.Cursor ;
+import org.apache.eve.backend.BackendException ;
+
+import jdbm.helper.Tuple ;
+
+
+/**
+ * Cursor that iterates over duplicate values nested into a value using a
+ * TreeSet.
+ *
+ * @warning The Tuple returned by this Cursor 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 advance() call.
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.3 $
+ */
+public class DupsCursor
+    extends Cursor
+{
+    private final Tuple m_returned = new Tuple() ;
+    private final Tuple m_prefetched = new Tuple() ;
+    private final NoDupsCursor m_cursor ;
+
+    private Iterator m_iterator ;
+    private Tuple m_duplicates ;
+
+
+    DupsCursor(NoDupsCursor a_cursor)
+        throws BackendException
+    {
+        m_cursor = a_cursor ;
+
+        try {
+            // Protect against closed cursors
+            if(m_cursor.isClosed()) {
+                close() ;
+                return ;
+            }
+    
+            prefetch() ;
+        } catch(NamingException e) { /* NEVER THROWN */ }
+    }
+
+
+    private void prefetch()
+        throws BackendException, NamingException
+    {
+        while(null == m_iterator || !m_iterator.hasNext()) {
+            if(m_cursor.hasMoreElements()) {
+                m_duplicates = (Tuple) m_cursor.next() ;
+                TreeSet l_set = (TreeSet) m_duplicates.getValue() ;
+
+                if(m_cursor.doAscendingScan()) {
+                    m_iterator = l_set.iterator() ;
+                } else {
+                    ArrayList l_list = new ArrayList(l_set.size()) ;
+                    l_list.addAll(l_set) ;
+                    Collections.reverse(l_list) ;
+                    m_iterator = l_list.iterator() ;
+                }
+            } else {
+                close() ;
+                return ;
+            }
+        }
+
+        m_prefetched.setKey(m_duplicates.getKey()) ;
+        m_prefetched.setValue(m_iterator.next()) ;
+    }
+
+
+    public Object advance()
+        throws BackendException, NamingException
+    {
+        m_returned.setKey(m_prefetched.getKey()) ;
+        m_returned.setValue(m_prefetched.getValue()) ;
+
+        prefetch() ;
+
+        return m_returned ;
+    }
+
+
+    public boolean canAdvance()
+    {
+        return !isClosed() ;
+    }
+
+
+    public void freeResources() { /* Does nothing! */ }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/JdbmTable.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/JdbmTable.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,989 @@
+/*
+ * $Id: JdbmTable.java,v 1.13 2003/03/13 18:27:31 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.table ;
+
+
+import java.util.TreeSet ;
+import java.util.SortedSet ;
+import java.util.ArrayList ;
+import java.util.Collections ;
+import java.io.IOException ;
+
+import org.apache.eve.backend.Cursor ;
+import org.apache.eve.backend.EmptyCursor ;
+import org.apache.eve.backend.SingletonCursor ;
+import org.apache.eve.backend.BackendException ;
+
+import jdbm.helper.MRU ;
+import jdbm.btree.BTree ;
+import jdbm.helper.Tuple ;
+import jdbm.helper.Comparator ;
+import jdbm.helper.ObjectCache ;
+import jdbm.helper.TupleBrowser ;
+import jdbm.recman.RecordManager ;
+
+import org.apache.avalon.framework.ExceptionUtil ;
+import org.apache.avalon.framework.logger.AbstractLogEnabled ;
+
+
+/**
+ * A jdbm Btree wrapper that enables duplicate sorted keys using collections.
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.13 $
+ */
+public class JdbmTable
+    extends AbstractLogEnabled implements Table
+{
+    private static final int CACHE_SIZE = 1000 ;
+    private static final String SZSUFFIX = "_btree_sz" ;
+
+    private final String m_name ;
+    private final RecordManager m_recMan ;
+    private final boolean allowsDuplicates ;
+    private final TupleComparator m_comparator ;
+
+    private int m_count = 0 ;
+    private BTree m_bt ;
+    private TupleRenderer m_renderer ;
+
+
+    //////////////////
+    // Constructors //
+    //////////////////
+
+    /**
+     * A jdbm Btree backed table.
+     */
+    public JdbmTable(String a_name, boolean allowsDuplicates,
+        RecordManager a_manager, TupleComparator a_comparator)
+        throws BackendException
+    {
+        m_name = a_name ;
+        m_recMan = a_manager ;
+        m_comparator = a_comparator ;
+        this.allowsDuplicates = allowsDuplicates ;
+
+        try {
+            ObjectCache m_cache =
+                new ObjectCache(m_recMan, new MRU(CACHE_SIZE)) ;
+    
+            long l_recId = m_recMan.getNamedObject(m_name) ;
+            if(l_recId != 0) {
+                m_bt = BTree.load(m_recMan, m_cache, l_recId) ;
+                l_recId = m_recMan.getNamedObject(m_name + SZSUFFIX) ;
+                m_count = ((Integer) m_recMan.fetchObject(l_recId)).intValue() ;
+            } else {
+                m_bt = new BTree(m_recMan, m_cache,
+                    (jdbm.helper.Comparator) a_comparator.getKeyComparator()) ;
+                l_recId = m_bt.getRecid() ;
+                m_recMan.setNamedObject(m_name, l_recId) ;
+
+				l_recId = m_recMan.insert(new Integer(0)) ;
+				m_recMan.setNamedObject(m_name + SZSUFFIX, l_recId) ;
+            }
+        } catch(Throwable e) {
+            throw new BackendException("Failed to load/create Btree:\n"
+                + ExceptionUtil.printStackTrace(e), e) ;
+        }
+    }
+
+
+    /**
+     * A jdbm Btree backed table.
+     */
+    public JdbmTable(String a_name, RecordManager a_manager,
+        TableComparator a_keyComparator)
+        throws BackendException
+    {
+        this(a_name, false, a_manager, new KeyOnlyComparator(a_keyComparator)) ;
+    }
+
+
+    /**
+     * 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.
+     */
+    public TupleComparator getComparator()
+    {
+        return m_comparator ;
+    }
+
+
+    /**
+     * Checks to see if this Table has enabled the use of duplicate keys.
+     *
+     * @return true if duplicate keys are enabled, false otherwise.
+     */
+	public boolean isDupsEnabled()
+    {
+        return allowsDuplicates;
+    }
+
+
+    /**
+     * Gets the name of this Table.
+     *
+     * @return the name
+     */
+    public String getName()
+    {
+        return m_name ;
+    }
+
+
+    /**
+     * Gets the data renderer used by this Table to display or log records keys
+     * and values.
+     *
+     * @return the renderer used
+     */
+    public TupleRenderer getRenderer()
+    {
+        return m_renderer ;
+    }
+
+
+    /**
+     * Sets the data renderer to by used by this Table to display or log record
+     * keys and values.
+     *
+     * @param a_renderer the DataRenderer instance to used as the renderer.
+     */
+    public void setRenderer(TupleRenderer a_renderer)
+    {
+        m_renderer = a_renderer ;
+    }
+
+
+    /**
+     * Checks to see if this Table has enabled sorting on the values of
+     * duplicate keys.  This will always return true but may change after
+     * this release.
+     *
+     * @return true if duplicate key values are sorted, false otherwise.
+     */
+	public boolean isSortedDupsEnabled()
+    {
+        // If duplicates are enabled than duplicates will be maintained in
+        // sorted order.
+        return allowsDuplicates ;
+    }
+
+
+    /**
+     * This operation is not supported by this implementation.  Will always
+     * throw a UnsupportedOperationException.
+     *
+     * @param a_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 a_key.
+     * @throws BackendException if there is a failure to read the underlying Db
+     */
+    public int count(Object a_key, boolean isGreaterThan)
+        throws BackendException
+    {
+        throw new UnsupportedOperationException() ;
+    }
+
+
+    /**
+     * Gets the count of the number of records in this Table with a specific
+     * key: returns the number of duplicates for a key.
+     *
+     * @param a_key the Object key to count.
+     * @return the number of duplicate records for a key.
+     * @throws BackendException if there is a failure to read the underlying Db
+     */
+    public int count(Object a_key)
+        throws BackendException
+    {
+        if(!allowsDuplicates) {
+            getLogger().warn("JdbmTable.count(Object):"
+                + " Should not be calling this method for tables that"
+                + " do not support duplicates.") ;
+
+            if(null == getRaw(a_key)) {
+                return 0 ;
+            } else {
+                return 1 ;
+            }
+        }
+
+        TreeSet l_set = (TreeSet) getRaw(a_key) ;
+
+        if(l_set != null) {
+            return l_set.size() ;
+        }
+
+        return 0 ;
+    }
+
+
+    /**
+     *
+     * @return the number of keys
+     * @throws BackendException if there is a failure to read the underlying Db
+     */
+    public int count()
+        throws BackendException
+    {
+        return m_count ;
+    }
+
+
+    /**
+     * 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 a_key the key of the record
+     * @return the value of the record with a_key if a_key exists or null if
+     * no such record exists.
+     * @throws BackendException if there is a failure to read the underlying Db
+     */
+    public Object get(Object a_key)
+        throws BackendException
+    {
+        if(allowsDuplicates) {
+            TreeSet l_set = (TreeSet) getRaw(a_key) ;
+            if(null == l_set || l_set.size() == 0) {
+                return null ;
+            } else {
+                return l_set.first() ;
+            }
+        }
+
+        return getRaw(a_key) ;
+    }
+
+
+    /**
+     * 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.
+     *
+     * @param a_key the key Object
+     * @param a_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 BackendException if there is a failure to read the underlying Db
+     * or if the underlying Db does not allow sorted duplicate values.
+     */
+	public boolean has(Object a_key, Object a_val, boolean isGreaterThan)
+        throws BackendException
+    {
+        if(!allowsDuplicates) {
+            getLogger().warn("JdbmTable.has(Object, Object, boolean):"
+                + " Should not be calling this method for tables that"
+                + " do not support duplicates.") ;
+
+            Object l_val = getRaw(a_key) ;
+
+            // key does not exist so return nothing
+            if(null == l_val) {
+				return false ;
+            }
+            // l_val == a_val return tuple
+            else if(a_val.equals(l_val)) {
+                return true ;
+            }
+			// l_val >= a_val and test is for greater then return tuple
+            else if(m_comparator.compareValue(l_val, a_val) >= 1 &&
+                isGreaterThan)
+            {
+				return true ;
+            }
+			// l_val <= a_val and test is for lesser then return tuple
+            else if(m_comparator.compareValue(l_val, a_val) <= 1 &&
+                !isGreaterThan)
+            {
+				return true ;
+        	}
+            // key's value does not equal a_val and conditions not satisfied.
+            else {
+                return false ;
+            }
+        }
+
+        TreeSet l_set = (TreeSet) getRaw(a_key) ;
+        if(null == l_set || l_set.size() == 0) {
+            return false ;
+        }
+
+        SortedSet l_subset = null ;
+        if(isGreaterThan) {
+            l_subset = l_set.tailSet(a_val) ;
+        } else {
+            l_subset = l_set.headSet(a_val) ;
+        }
+
+        if(l_subset.size() > 0 || l_set.contains(a_val)) {
+            return true ;
+        }
+
+        return false ;
+    }
+
+
+    /**
+     * 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.
+     *
+     * @param a_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 BackendException if there is a failure to read the underlying Db
+     */
+	public boolean has(Object a_key, boolean isGreaterThan)
+        throws BackendException
+    {
+        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.
+            Tuple l_tuple = m_bt.findGreaterOrEqual(a_key) ;
+    
+            // Test for equality first since it satisfies both greater/less than
+            if(null != l_tuple &&
+                m_comparator.compareKey(l_tuple.getKey(), a_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 == l_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 l_browser = null ;
+            if(null == l_tuple) {
+                // findGreaterOrEqual failed so we create a tuple and scan from
+                // the lowest values up via getNext comparing each key to a_key
+                l_tuple = new Tuple() ;
+                l_browser = m_bt.browse() ;
+    
+                // We should at most have to read one key.  If 1st key is not
+                // less than or equal to a_key then all keys are > a_key
+                // since the keys are assorted in ascending order based on the
+                // comparator.
+                while(l_browser.getNext(l_tuple)) {
+                    if(m_comparator.compareKey(l_tuple.getKey(), a_key) <= 0) {
+                        return true ;
+                    } else { // Short the search to prevent wasted cycling
+                        return false ;
+                    }
+                }
+            } else {
+                // findGreaterOrEqual succeeded so use the existing tuple and
+                // scan the down from the highest key less than a_key via
+                // getPrevious while comparing each key to a_key.
+                l_browser = m_bt.browse(l_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 a_key.
+                if(m_comparator.compareKey(l_tuple.getKey(), a_key) <= 0) {
+                    return true ;
+                }
+                l_browser.getNext(l_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(l_browser.getPrevious(l_tuple)) {
+                    if(m_comparator.compareKey(l_tuple.getKey(), a_key) <= 0) {
+                        return true ;
+                    }
+                }
+            }
+        } catch(IOException e) {
+            String l_msg = "Failed to lookup whether a key " ;
+            if(isGreaterThan) {
+                l_msg += "greater " ;
+            } else {
+                l_msg += "less " ;
+            }
+            l_msg += "than or equal to a key " + renderKey(a_key)
+                + " exists:\n" + ExceptionUtil.printStackTrace(e) ;
+            throw new BackendException(l_msg, e) ;
+        }
+
+        return false ;
+    }
+
+
+    /**
+     * Checks to see if this table has a key with a specific value.
+     *
+     * @param a_key the key Object to check for
+     * @param a_value the value Object to check for
+     * @return true if a record with the key and value exists, false otherwise.
+     * @throws BackendException if there is a failure to read the underlying Db
+     */
+    public boolean has(Object a_key, Object a_value)
+        throws BackendException
+    {
+        if(allowsDuplicates) {
+            TreeSet l_set = (TreeSet) getRaw(a_key) ;
+            if(null == l_set) {
+                return false ;
+            }
+
+            return l_set.contains(a_value) ;
+        }
+
+        Object l_obj = getRaw(a_key) ;
+        if(null == l_obj) {
+            return false ;
+        }
+
+        return l_obj.equals(a_value) ;
+    }
+
+
+    /**
+     * 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 a_key the Object of the key to check for
+     * @return true if the key exists, false otherwise.
+     * @throws BackendException if there is a failure to read the underlying Db
+     */
+    public boolean has(Object a_key)
+        throws BackendException
+    {
+        return getRaw(a_key) != null ;
+    }
+
+
+    /**
+     * Puts a record into this Table.
+     *
+     * @param a_key the key of the record
+     * @param a_value the value of the record.
+     * @return the last value present for a_key or null if this the key did not
+     * exist before. For tables allowing duplicates the return value is null.
+     * @throws BackendException if there is a failure to read or write to
+     * the underlying Db
+     */
+    public Object put(Object a_key, Object a_value)
+        throws BackendException
+    {
+        Object l_replaced = null ;
+
+        if(allowsDuplicates) {
+            TreeSet l_set = (TreeSet) getRaw(a_key) ;
+            if(null == l_set) {
+                l_set = new TreeSet(m_comparator.getValueComparator()) ;
+            } else if(l_set.contains(a_value)) {
+                return a_value ;
+            }
+
+            l_set.add(a_value) ;
+            putRaw(a_key, l_set, true) ;
+            m_count++ ;
+            return null ;
+        }
+
+        l_replaced = putRaw(a_key, a_value, true) ;
+
+        if(null == l_replaced) {
+            m_count++ ;
+        }
+
+        return l_replaced ;
+    }
+
+
+    /**
+     * Removes a single specific record with a_key and a_value from this Table.
+     *
+     * @param the key of the record to remove.
+     * @param the value of the record to remove.
+     * @return a_value if (a_key, a_value) exists to be removed else null
+     * @throws BackendException if there is a failure to read or write to
+     * the underlying Db
+     */
+    public Object remove(Object a_key, Object a_value)
+        throws BackendException
+    {
+        if(allowsDuplicates) {
+            TreeSet l_set = (TreeSet) getRaw(a_key) ;
+
+            if(null == l_set) {
+                return null ;
+            }
+
+            // If removal succeeds then remove if set is empty else replace it
+            if(l_set.remove(a_value)) {
+                if(l_set.isEmpty()) {
+                    removeRaw(a_key) ;
+                } else {
+                    putRaw(a_key, l_set, true) ;
+                }
+
+                // Decrement counter if removal occurs.
+                m_count-- ;
+                return a_value ;
+            }
+
+            return null ;
+        }
+
+        Object l_removed = null ;
+
+        // Remove the value only if it is the same as a_value.
+        if(getRaw(a_key).equals(a_value)) {
+            return removeRaw(a_key) ;
+        }
+
+        return null ;
+    }
+
+
+    /**
+     * Removes all records with a_key from this Table.
+     *
+     * @param the key of the records to remove.
+     * @return if a_key exists its value is returned.  The value will be a
+     * TreeSet containing sorted duplicate values if this table allows
+     * duplicates.
+     * @throws BackendException if there is a failure to read or write to
+     * the underlying Db
+     */
+    public Object remove(Object a_key)
+        throws BackendException
+    {
+        Object l_returned = removeRaw(a_key) ;
+
+        if(null == l_returned) {
+            return null ;
+        }
+
+        if(allowsDuplicates) {
+            TreeSet l_set = (TreeSet) l_returned ;
+            this.m_count -= l_set.size() ;
+            return l_set.first() ;
+        }
+
+        this.m_count-- ;
+        return l_returned ;
+    }
+
+
+    //////////////////////
+    // Cursor Overloads //
+    //////////////////////
+
+
+    /**
+     * Sets a cursor to the first record in the Table and enables single
+     * next steps across all records.
+     *
+     * @throws BackendException if the underlying cursor could not be set.
+     */
+    public Cursor getCursor()
+        throws BackendException
+    {
+        Cursor l_cursor = null ;
+
+        try {
+            l_cursor = new NoDupsCursor(m_bt.browse(), true) ;
+            l_cursor.enableLogging(getLogger()) ;
+        } catch(IOException e) {
+            throw new BackendException("Could not create cursor over table "
+                + m_name + ":\n" + ExceptionUtil.printStackTrace(e), e) ;
+        }
+
+        if(allowsDuplicates) {
+            l_cursor = new DupsCursor((NoDupsCursor) l_cursor) ;
+            l_cursor.enableLogging(getLogger()) ;
+            return l_cursor ;
+        }
+
+        return l_cursor ;
+    }
+
+
+    /**
+     * Sets a cursor to the first record in the Table with a key value of
+     * a_key and enables single next steps across all duplicate records with
+     * this key.  This cursor will only iterate over duplicates of the key.
+     *
+     * @param a_key the key to iterate over
+     * @throws BackendException if the underlying cursor could not be set
+     */
+    public Cursor getCursor(Object a_key)
+        throws BackendException
+    {
+        if(!allowsDuplicates) {
+            getLogger().warn("JdbmTable.getCursor(Object):"
+                + " Should not be calling this method for tables that"
+                + " do not support duplicates.") ;
+
+            Object l_val = getRaw(a_key) ;
+            if(null == l_val) {
+                return new EmptyCursor() ;
+            } else {
+            	return new SingletonCursor(new Tuple(a_key, getRaw(a_key))) ;
+            }
+        }
+
+        TreeSet l_set = (TreeSet) getRaw(a_key) ;
+        if(l_set == null) {
+            return new EmptyCursor() ;
+        }
+
+        Cursor l_cursor = new TupleIteratorCursor(a_key, l_set.iterator()) ;
+        l_cursor.enableLogging(getLogger()) ;
+        return l_cursor ;
+    }
+
+
+    /**
+     * Sets a cursor to the first record in the Table with a key value
+     * greater/less than or equal to a_key and enables single next steps across
+     * all records with key values equal to or less/greater than a_key.
+     *
+     * @warning This cursor operation has no meaning for database table
+     * types other than the btree type since it relies on sorted keys.
+     * @param a_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 a_key argument, but if false this cursor
+     * iterates down over descending keys less than or equal to a_key argument.
+     * @throws BackendException if the underlying cursor could not be set
+     */
+    public Cursor getCursor(Object a_key, boolean isGreaterThan)
+        throws BackendException
+    {
+        Cursor l_cursor = null ;
+
+        try {
+            if(isGreaterThan) {
+	            l_cursor = new NoDupsCursor(m_bt.browse(a_key), isGreaterThan) ;
+            } else {
+                /* According to the jdbm docs a browser is positioned right
+                 * before a key greater than or equal to a_key.  getNext() will
+                 * return the next tuple with a key greater than or equal to
+                 * a_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
+                 * a_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.
+                 */
+				Tuple l_tuple = new Tuple() ;
+				TupleBrowser l_browser = m_bt.browse(a_key) ;
+                if(l_browser.getNext(l_tuple)) {
+                    Object l_greaterKey = l_tuple.getKey() ;
+                    if(0 != m_comparator.compareKey(a_key, l_greaterKey)) {
+                        // Make sure we don't return l_greaterKey in cursor
+                        l_browser.getPrevious(l_tuple) ;
+                    }
+                }
+
+                // If l_greaterKey != a_key above then it will not be returned.
+                l_cursor = new NoDupsCursor(l_browser, isGreaterThan) ;
+            }
+        } catch(IOException e) {
+            throw new BackendException("Failed to get TupleBrowser on table "
+                + m_name + " using key " + renderKey(a_key) + ":\n"
+                + ExceptionUtil.printStackTrace(e), e) ;
+        }
+
+        l_cursor.enableLogging(getLogger()) ;
+        if(allowsDuplicates) {
+            l_cursor = new DupsCursor((NoDupsCursor) l_cursor) ;
+            l_cursor.enableLogging(getLogger()) ;
+        }
+
+        return l_cursor ;
+    }
+
+
+    /**
+     * Sets a cursor to the first record in the Table with a key equal to
+     * the a_key argument whose value is greater/less than or equal to a_val and
+     * enables single next steps across all records with key equal to a_key.
+     * Hence this cursor will only iterate over duplicate keys where values are
+     * less/greater than or equal to a_val.
+     *
+     * @warning the underlying Db must have sorted duplicates enabled as in an
+     * Index.
+     * @param a_key the key to use to position this cursor to record with a key
+     * equal to it.
+     * @param a_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 a_val argument, but if false this cursor
+     * iterates down over values less than or equal to a_val argument
+     * descending from the highest values going down.
+     * @throws BackendException 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.
+     */
+    public Cursor getCursor(Object a_key, Object a_val, boolean isGreaterThan)
+        throws BackendException
+    {
+        if(!allowsDuplicates) {
+            getLogger().warn("JdbmTable.getCursor(Object, Object, boolean):"
+                + " Should not be calling this method for tables that"
+                + " do not support duplicates.") ;
+
+            Object l_val = getRaw(a_key) ;
+
+            // key does not exist so return nothing
+            if(null == l_val) {
+				return new EmptyCursor() ;
+            }
+            // l_val == a_val return tuple
+            else if(a_val.equals(l_val)) {
+                return new SingletonCursor(new Tuple(a_key, a_val)) ;
+            }
+			// l_val >= a_val and test is for greater then return tuple
+            else if(m_comparator.compareValue(l_val, a_val) >= 1 &&
+                isGreaterThan)
+            {
+				return new SingletonCursor(new Tuple(a_key, a_val)) ;
+            }
+			// l_val <= a_val and test is for lesser then return tuple
+            else if(m_comparator.compareValue(l_val, a_val) <= 1 &&
+                !isGreaterThan)
+            {
+				return new SingletonCursor(new Tuple(a_key, a_val)) ;
+        	}
+            // key's value does not equal a_val and conditions not satisfied.
+            else {
+                return new EmptyCursor() ;
+            }
+        }
+
+        TreeSet l_set = (TreeSet) getRaw(a_key) ;
+        if(l_set == null) {
+            return new EmptyCursor() ;
+        }
+
+        if(isGreaterThan) {
+            return new TupleIteratorCursor(a_key,
+                l_set.tailSet(a_val).iterator()) ;
+        } else {
+            // Get all values from the smallest upto a_val and put them into
+            // a list.  They will be in ascending order so we need to reverse
+            // the list after adding a_val which is not included in headSet.
+            SortedSet l_headset = l_set.headSet(a_val) ;
+            ArrayList l_list = new ArrayList(l_set.size() + 1) ;
+            l_list.addAll(l_headset) ;
+
+            // Add largest value (a_val) if it is in the set.  TreeSet.headSet
+            // does not get a_val if a_val is in the set.  So we add it now to
+            // the end of the list.  List is now ascending from smallest to
+            // a_val
+            if(l_set.contains(a_val)) {
+                l_list.add(a_val) ;
+            }
+
+            // Reverse the list now we have descending values from a_val to the
+            // smallest value that a_key has.  Return tuple cursor over list.
+            Collections.reverse(l_list) ;
+            return new TupleIteratorCursor(a_key, l_list.iterator()) ;
+        }
+    }
+
+
+    ////////////////////////////
+    // Maintenance Operations //
+    ////////////////////////////
+
+
+    /**
+     * Saves the count of this records to the database file but does not really
+     * close the file since other tables may still be open.  This is because for
+     * Jdbm the RecordManager closes the file.  Many btrees can be stored in a
+     * file.  So this operation mearly performs clean up for this btree.
+     * 
+     * @throws BackendException if there is a failure to save the store the
+     * count data.
+     */
+    public synchronized void close()
+    {
+        try {
+            sync() ;
+        } catch(BackendException e) {
+            getLogger().error("Faild to update and close database:\n", e) ;
+        }
+    }
+
+
+    /**
+     * Does nothing really.  This is because for Jdbm the RecordManager needs
+     * to perform synchronization for the whole file.  This table may be one
+     * btree in that file.  So sychronization is handled higher up.
+     */
+    public void sync() throws BackendException
+    {
+        try {
+            long l_recId = m_recMan.getNamedObject(m_name + SZSUFFIX) ;
+            if(0 == l_recId) {
+				l_recId = m_recMan.insert(new Integer(m_count)) ;
+            } else {
+				m_recMan.update(l_recId, new Integer(m_count)) ;
+            }
+        } catch(IOException e) {
+            throw new BackendException("Failed to update and close database:\n"
+                + ExceptionUtil.printStackTrace(e), e) ;
+        }
+    }
+
+
+    /////////////////////////////
+    // Private Utility Methods //
+    /////////////////////////////
+
+
+    private String render(Object a_key, Object a_value)
+    {
+        StringBuffer l_buf = new StringBuffer() ;
+
+        l_buf.append("('") ;
+        if(null == m_renderer) {
+            l_buf.append(a_key.toString()) ;
+        } else {
+            l_buf.append(m_renderer.getKeyString(a_key)) ;
+        }
+        l_buf.append("', '") ;
+        if(null == m_renderer) {
+            l_buf.append(a_value.toString()) ;
+        } else {
+            l_buf.append(m_renderer.getValueString(a_value)) ;
+        }
+        l_buf.append("')") ;
+
+        return l_buf.toString() ;
+    }
+
+
+    private String renderKey(Object a_obj)
+    {
+        StringBuffer l_buf = new StringBuffer() ;
+
+        l_buf.append('\'') ;
+        if(null == m_renderer) {
+            l_buf.append(a_obj.toString()) ;
+        } else {
+            l_buf.append(m_renderer.getKeyString(a_obj)) ;
+        }
+        l_buf.append('\'') ;
+
+        return l_buf.toString() ;
+    }
+
+
+    private String renderValue(Object a_obj)
+    {
+        StringBuffer l_buf = new StringBuffer() ;
+
+        l_buf.append('\'') ;
+        if(null == m_renderer) {
+            l_buf.append(a_obj.toString()) ;
+        } else {
+            l_buf.append(m_renderer.getValueString(a_obj)) ;
+        }
+        l_buf.append('\'') ;
+
+        return l_buf.toString() ;
+    }
+
+
+    /**
+     * 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 a_key the key of the record
+     * @return the value of the Btree tuple which is a TreeSet if this Table
+     * supports duplicates.
+     * @throws BackendException if there is a failure to read the underlying Db
+     */
+    private Object getRaw(Object a_key)
+        throws BackendException
+    {
+        Object l_value = null ;
+
+        try {
+            l_value = m_bt.find(a_key) ;
+        } catch(IOException e) {
+            throw new BackendException("Failed to get value for key "
+                + renderKey(a_key) + ":\n"
+                + ExceptionUtil.printStackTrace(e), e) ;
+        }
+
+        return l_value ;
+    }
+
+
+    private Object putRaw(Object a_key, Object a_value, boolean doReplace)
+        throws BackendException
+    {
+        Object l_replaced = null ;
+
+        try {
+            l_replaced = m_bt.insert(a_key, a_value, doReplace) ;
+        } catch(IOException e) {
+            throw new BackendException("Failed to insert key value pair "
+                + render(a_key, a_value) + " into table:\n"
+                + ExceptionUtil.printStackTrace(e), e) ;
+        }
+
+        return l_replaced ;
+    }
+
+
+    private Object removeRaw(Object a_key)
+        throws BackendException
+    {
+        Object l_removed = null ;
+
+        try {
+            l_removed = m_bt.remove(a_key) ;
+        } catch(IOException e) {
+            throw new BackendException(
+                "Failed to remove record with key " + renderKey(a_key)
+                + ":\n" + ExceptionUtil.printStackTrace(e), e) ;
+        }
+
+        return l_removed ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/KeyOnlyComparator.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/KeyOnlyComparator.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,78 @@
+/*
+ * $Id: KeyOnlyComparator.java,v 1.2 2003/03/13 18:27:32 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.table ;
+
+
+public class KeyOnlyComparator
+    implements TupleComparator
+{
+    TableComparator m_keyComparator = null ;
+
+
+    public KeyOnlyComparator(TableComparator a_comparator)
+    {
+        m_keyComparator = a_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 TableComparator getKeyComparator()
+    {
+        return m_keyComparator ;
+    }
+
+
+    /**
+     * Will throw an UnsupportedOperationException every time.
+     *
+     * @throws UnsuporredOperation every time.
+     */
+	public TableComparator getValueComparator()
+    {
+        throw new UnsupportedOperationException() ;
+    }
+
+
+    /**
+     * Compares key Object to determine their sorting order returning a
+     * value = to, < or > than 0.
+     *
+     * @param a_key1 the first key to compare
+     * @param a_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 a_key1, Object a_key2)
+    {
+        return m_keyComparator.compare(a_key1, a_key2) ;
+    }
+
+
+    /**
+     * Comparse value Objects to determine their sorting order returning a
+     * value = to, < or > than 0.
+     *
+     * @param a_value1 the first value to compare
+     * @param a_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 a_value1, Object a_value2)
+    {
+        throw new UnsupportedOperationException() ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/MasterTable.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/MasterTable.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,189 @@
+/*
+ * $Id: MasterTable.java,v 1.3 2003/03/13 18:27:33 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.table ;
+
+
+import java.io.File ;
+import java.math.BigInteger ;
+import javax.naming.NamingException ;
+
+import org.apache.eve.backend.BackendException ;
+import jdbm.recman.RecordManager;
+import org.apache.eve.backend.jdbm.index.BigIntegerComparator;
+import org.apache.eve.backend.jdbm.index.StringComparator;
+import org.apache.eve.backend.jdbm.index.IndexComparator;
+
+
+/**
+ * The master table used to store LDIF based entries.
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.3 $
+ * @testcase org.apache.eve.backend.berkeley.table.TestMasterTable
+ */
+public class MasterTable
+    extends JdbmTable
+{
+    /** the name of the dbf file for this table */
+	public static final String DBF = "master" ;
+    /** the sequence key - stores last sequence value in the admin table */
+    public static final String m_sequence = "__sequence__" ;
+
+    private JdbmTable m_adminTbl = null ;
+
+
+    /**
+     * Creates the master entry table using a Berkeley Db for the backing store.
+     *
+     * @param a_wkdirPath the working directory path where we create the
+     * berkeley database files.
+     * @throws BackendException if there is an error opening the Db file.
+     */
+	public MasterTable(RecordManager a_recMan)
+        throws BackendException
+    {
+        super(DBF, a_recMan, new BigIntegerComparator()) ;
+        m_adminTbl = new JdbmTable("admin", a_recMan, IndexComparator.strComp) ;
+        String l_seqValue = (String) m_adminTbl.get(m_sequence) ;
+        if(null == l_seqValue) {
+            m_adminTbl.put(m_sequence, BigInteger.ZERO.toString()) ;
+        }
+    }
+
+
+    /**
+     * Gets an LDIF record out of this MasterTable corresponding to an entry
+     * with a id.
+     *
+     * @param an_id the BigInteger id of the entry to retrieve.
+     * @return the LDIF of the entry with operational attributes and all.
+     * @throws BackendException if there is a read error on the underlying Db.
+     */
+    public String get(BigInteger an_id)
+        throws BackendException
+    {
+        return (String) super.get(an_id) ;
+    }
+
+
+    /**
+     * Puts a ldif into the master table at an index specified by an_id.  This
+     * is used both to create new entries and update existing ones.
+     *
+     * @param a_ldif the LDIF of the entry with operational attributes and all.
+     * @param an_id the BigInteger id of the entry to put.
+     * @throws BackendException if there is a write error on the underlying Db.
+     */
+    public String put(String a_ldif, BigInteger an_id)
+        throws BackendException
+    {
+        return (String) super.put(an_id, a_ldif) ;
+    }
+
+
+    /**
+     * Deletes a entry from the master table at an index specified by an_id.
+     *
+     * @param an_id the BigInteger id of the entry to delete.
+     * @throws BackendException if there is a write error on the underlying Db.
+     */
+    public String del(BigInteger an_id)
+        throws BackendException, NamingException
+    {
+        return (String) super.remove(an_id) ;
+    }
+
+
+    /**
+     * Get's the current id value from this master database's sequence without
+     * affecting the seq.
+     *
+     * @return the current value.
+     * @throws BackendException if the admin table storing sequences cannot be
+     * read.
+     */
+    public BigInteger getCurrentId()
+        throws BackendException
+    {
+        BigInteger l_id = null ;
+
+        synchronized(m_adminTbl) {
+            l_id = new BigInteger((String) m_adminTbl.get(m_sequence)) ;
+            if(null == l_id) {
+                m_adminTbl.put(m_sequence, BigInteger.ZERO.toString()) ;
+                l_id = BigInteger.ZERO ;
+            }
+        }
+
+        return l_id ;
+    }
+
+
+    /**
+     * Get's the next value from this SequenceBDb.  This has the side-effect of
+     * changing the current sequence values perminantly in memory and on disk.
+     *
+     * @return the current value incremented by one.
+     * @throws BackendException if the admin table storing sequences cannot be
+     * read and writen to.
+     */
+    public BigInteger getNextId()
+        throws BackendException
+    {
+        BigInteger l_lastVal = null ;
+        BigInteger l_nextVal = null ;
+
+        synchronized(m_adminTbl) {
+            l_lastVal = new BigInteger((String) m_adminTbl.get(m_sequence)) ;
+            if(null == l_lastVal) {
+                m_adminTbl.put(m_sequence, BigInteger.ONE.toString()) ;
+                return BigInteger.ONE ;
+            } else {
+                l_nextVal = l_lastVal.add(BigInteger.ONE) ;
+                m_adminTbl.put(m_sequence, l_nextVal.toString()) ;
+            }
+        }
+
+        return (l_nextVal) ;
+    }
+
+
+    /**
+     * Gets a persistant property stored in the admin table of this MasterTable.
+     *
+     * @param a_property the key of the property to get the value of
+     * @return the value of the property
+     * @throws BackendException when the underlying admin table cannot be read
+     */
+	public String getProperty(String a_property)
+        throws BackendException
+    {
+        synchronized(m_adminTbl) {
+    	    return (String) m_adminTbl.get(a_property) ;
+        }
+    }
+
+
+    /**
+     * Sets a persistant property stored in the admin table of this MasterTable.
+     *
+     * @param a_property the key of the property to set the value of
+     * @param a_value the value of the property
+     * @throws BackendException when the underlying admin table cannot be writen
+     */
+	public void setProperty(String a_property, String a_value)
+        throws BackendException
+    {
+        synchronized(m_adminTbl) {
+            m_adminTbl.put(a_property, a_value) ;
+        }
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/NoDupsCursor.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/NoDupsCursor.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,108 @@
+/*
+ * $Id: NoDupsCursor.java,v 1.3 2003/03/13 18:27:33 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.table ;
+
+
+import org.apache.eve.backend.Cursor ;
+import org.apache.eve.backend.BackendException ;
+
+import jdbm.helper.TupleBrowser ;
+import jdbm.helper.Tuple;
+import java.io.IOException ;
+import javax.naming.NamingException ;
+
+import org.apache.avalon.framework.ExceptionUtil ;
+
+
+/**
+ * A simple cursor over a TupleBrowser on a table that does not allow
+ * duplicates.
+ * 
+ * @warning The Tuple returned by this Cursor 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 advance() call.
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.3 $
+ */
+public class NoDupsCursor
+    extends Cursor
+{
+    private final Tuple m_returned = new Tuple() ;
+    private final Tuple m_prefetched = new Tuple() ;
+    private final TupleBrowser m_browser ;
+    private final boolean doAscendingScan ;
+
+    protected boolean canAdvance = true ;
+
+
+    /**
+     * Creates a cursor over a TupleBrowser where duplicates are not expected.
+     */
+    NoDupsCursor(TupleBrowser a_browser, boolean doAscendingScan)
+        throws BackendException
+    {
+        m_browser = a_browser ;
+        this.doAscendingScan = doAscendingScan ;
+        prefetch() ;
+    }
+
+
+    boolean doAscendingScan()
+    {
+        return this.doAscendingScan ;
+    }
+
+
+    void prefetch()
+        throws BackendException
+    {
+        // Prefetch into tuple!
+        boolean isSuccess = false ;
+
+        try {
+            if(doAscendingScan) {
+                isSuccess = m_browser.getNext(m_prefetched) ;
+            } else {
+                isSuccess = m_browser.getPrevious(m_prefetched) ;
+            }
+        } catch(IOException e) {
+            throw new BackendException("Could not advance cursor due to "
+                + " TupleBrowser failure:\n"
+                + ExceptionUtil.printStackTrace(e)) ;
+        }
+
+        if(!isSuccess) {
+            canAdvance = false ;
+            try { close() ; } catch(NamingException e) { /* Never thrown */ }
+        }
+    }
+
+
+    protected Object advance()
+        throws BackendException
+    {
+        m_returned.setKey(m_prefetched.getKey()) ;
+        m_returned.setValue(m_prefetched.getValue()) ;
+
+        prefetch() ;
+
+        return m_returned ;
+    }
+
+
+    protected boolean canAdvance()
+    {
+        return canAdvance ;
+    }
+
+
+    public void freeResources() { /* Does nothing! */ }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/Table.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/Table.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,288 @@
+/*
+ * $Id: Table.java,v 1.2 2003/03/13 18:27:34 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.table ;
+
+
+import org.apache.eve.backend.Cursor ;
+import org.apache.eve.backend.BackendException ;
+
+
+/**
+ * A backend friendly wrapper around a jdbm BTree that transparent enables
+ * duplicates when the BTree does not support them.
+ * 
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.2 $
+ */
+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.
+     */
+    public TupleComparator getComparator() ;
+
+    /**
+     * Gets the data renderer used by this Table to display or log records keys
+     * and values.
+     *
+     * @return the renderer used
+     */
+    public TupleRenderer getRenderer() ;
+
+    /**
+     * Sets the data renderer to by used by this Table to display or log record
+     * keys and values.
+     *
+     * @param a_renderer the DataRenderer instance to used as the renderer.
+     */
+    public void setRenderer(TupleRenderer a_renderer) ;
+
+    /**
+     * Gets the name of this Table.
+     *
+     * @return the name
+     */
+    public String getName() ;
+
+    /**
+     * Checks to see if this Table has enabled the use of duplicate keys.
+     *
+     * @return true if duplicate keys are enabled, false otherwise.
+     */
+	public 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.
+     */
+	public 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 a_key the Object of the key to check for
+     * @return true if the key exists, false otherwise.
+     * @throws BackendException if there is a failure to read the underlying Db
+     */
+    public boolean has(Object a_key) throws BackendException ;
+
+    /**
+     * Checks to see if this table has a key with a specific value.
+     *
+     * @param a_key the key Object to check for
+     * @param a_value the value Object to check for
+     * @return true if a record with the key and value exists, false otherwise.
+     * @throws BackendException if there is a failure to read the underlying Db
+     */
+    public boolean has(Object a_key, Object a_value) throws BackendException ;
+
+    /**
+     * 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 a_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 BackendException if there is a failure to read the underlying Db,
+     * or if the underlying Db is not a Btree.
+     */
+	public boolean has(Object a_key, boolean isGreaterThan)
+        throws BackendException ;
+
+    /**
+     * 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.
+     *
+     * @param a_key the key Object
+     * @param a_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 BackendException 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.
+     */
+	public boolean has(Object a_key, Object a_val, boolean isGreaterThan)
+        throws BackendException ;
+
+    ////////////////////////////////////
+    // 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 a_key the key of the record
+     * @return the value of the record with a_key if a_key exists or null if
+     * no such record exists.
+     * @throws BackendException if there is a failure to read the underlying Db
+     */
+    public Object get(Object a_key) throws BackendException ;
+
+    /**
+     * Puts a record into this Table.
+     *
+     * @param a_key the key of the record
+     * @param a_value the value of the record.
+     * @return the last value present for a_key or null if this the key did not
+     * exist before.
+     * @throws BackendException if there is a failure to read or write to
+     * the underlying Db
+     */
+    public Object put(Object a_key, Object a_value) throws BackendException ;
+
+    /**
+     * Removes all records with a_key from this Table.
+     *
+     * @param the key of the records to remove.
+     * @throws BackendException if there is a failure to read or write to
+     * the underlying Db
+     */
+    public Object remove(Object a_key) throws BackendException ;
+
+    /**
+     * Removes a single specific record with a_key and a_value from this Table.
+     *
+     * @param the key of the record to remove.
+     * @param the value of the record to remove.
+     * @throws BackendException if there is a failure to read or write to
+     * the underlying Db
+     */
+    public Object remove(Object a_key, Object a_value) throws BackendException ;
+
+    //////////////////////
+    // Cursor Overloads //
+    //////////////////////
+
+    /**
+     * Sets a cursor to the first record in the Table and enables single
+     * next steps across all records.
+     *
+     * @throws BackendException if the underlying cursor could not be set.
+     */
+    public Cursor getCursor() throws BackendException ;
+
+    /**
+     * Sets a cursor to the first record in the Table with a key value of
+     * a_key and enables single next steps across all duplicate records with
+     * this key.  This cursor will only iterate over duplicates of the key.
+     *
+     * @param a_key the key to iterate over
+     * @throws BackendException if the underlying cursor could not be set
+     */
+    public Cursor getCursor(Object a_key) throws BackendException ;
+
+    /**
+     * Sets a cursor to the first record in the Table with a key value
+     * greater/less than or equal to a_key and enables single next steps across
+     * all records with key values equal to or less/greater than a_key.
+     *
+     * @warning This cursor operation has no meaning for database table
+     * types other than the btree type since it relies on sorted keys.
+     * @param a_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 a_key argument, but if false this cursor
+     * iterates down over descending keys less than or equal to a_key argument.
+     * @throws BackendException if the underlying cursor could not be set
+     */
+    public Cursor getCursor(Object a_key, boolean isGreaterThan)
+        throws BackendException ;
+
+    /**
+     * Sets a cursor to the first record in the Table with a key equal to
+     * the a_key argument whose value is greater/less than or equal to a_key and
+     * enables single next steps across all records with key equal to a_key.
+     * Hence this cursor will only iterate over duplicate keys where values are
+     * less than or greater than or equal to a_val.
+     *
+     * @warning the underlying Db must have sorted duplicates enabled as in an
+     * Index.
+     * @param a_key the key to use to position this cursor to record with a key
+     * equal to it.
+     * @param a_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 a_val argument, but if false this cursor
+     * iterates down over descending values less than or equal to a_val argument
+     * starting from the largest value going down.
+     * @throws BackendException 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.
+     */
+    public Cursor getCursor(Object a_key, Object a_val, boolean isGreaterThan)
+        throws BackendException ;
+
+    ////////////////////////////////
+    // Table Record Count Methods //
+    ////////////////////////////////
+
+    /**
+     * Gets the count of the number of records in this Table.
+     *
+     * @return the number of records
+     * @throws BackendException if there is a failure to read the underlying Db
+     */
+    public int count() throws BackendException ;
+
+    /**
+     * Gets the count of the number of records in this Table with a specific
+     * key: returns the number of duplicates for a key.
+     *
+     * @param a_key the Object key to count.
+     * @return the number of duplicate records for a key.
+     * @throws BackendException if there is a failure to read the underlying Db
+     */
+    public int count(Object a_key) throws BackendException ;
+
+    /**
+     * 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 a_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 a_key.
+     * @throws BackendException if there is a failure to read the underlying Db
+     */
+    public int count(Object a_key, boolean isGreaterThan) throws BackendException ;
+
+    /**
+     * Closes the underlying Db of this Table.
+     * 
+     * @throws BackendException if there is a failure to close the handle to
+     * the underlying Db file.
+     */
+    public void close() ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/TableComparator.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/TableComparator.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,26 @@
+/*
+ * $Id: TableComparator.java,v 1.2 2003/03/13 18:27:34 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.table ;
+
+
+/**
+ * Doc me!
+ * @todo Doc me!
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.2 $
+ */
+public abstract class TableComparator
+    extends jdbm.helper.Comparator
+    implements java.util.Comparator
+{
+    public abstract int compare( Object an_obj1, Object an_obj2 ) ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/TupleComparator.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/TupleComparator.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,64 @@
+/*
+ * $Id: TupleComparator.java,v 1.3 2003/03/13 18:27:34 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.table ;
+
+
+/**
+ * Used to compare the sorting order of binary data.
+ * 
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.3 $
+ */
+public interface TupleComparator
+{
+    /**
+     * 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.
+     */
+	TableComparator 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.
+     */
+	TableComparator getValueComparator() ;
+
+
+    /**
+     * Compares key Object to determine their sorting order returning a
+     * value = to, < or > than 0.
+     *
+     * @param a_key1 the first key to compare
+     * @param a_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 a_key1, Object a_key2) ;
+
+
+    /**
+     * Comparse value Objects to determine their sorting order returning a
+     * value = to, < or > than 0.
+     *
+     * @param a_value1 the first value to compare
+     * @param a_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 a_value1, Object a_value2) ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/TupleIteratorCursor.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/TupleIteratorCursor.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,64 @@
+/*
+ * $Id: TupleIteratorCursor.java,v 1.2 2003/03/13 18:27:35 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.table ;
+
+
+import java.util.Iterator ;
+import org.apache.eve.backend.Cursor;
+import jdbm.helper.Tuple;
+
+
+/**
+ * A cursor using an underlying Iterator.
+ * 
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.2 $
+ */
+public class TupleIteratorCursor
+    extends Cursor
+{
+    private final Object m_key ;
+    private final Iterator m_iterator ;
+    private final Tuple m_tuple = new Tuple() ;
+
+
+    /**
+     * Creates a cursor over an Iterator.
+     * 
+     * @param a_iterator the underlying iterator this cursor uses.
+     */
+    public TupleIteratorCursor(Object a_key, Iterator a_iterator)
+    {
+        m_key = a_key ;
+        m_tuple.setKey(a_key) ;
+        m_iterator = a_iterator ;
+    }
+
+
+    /** Returns iterator.next() */
+    public Object advance()
+    {
+        m_tuple.setKey(m_key) ;
+        m_tuple.setValue(m_iterator.next()) ;
+        return m_tuple ;
+    }
+
+
+    /** Returns iterator.hasNext() */
+    public boolean canAdvance()
+    {
+        return m_iterator.hasNext() ;
+    }
+
+
+    /** Does nothing */
+    public void freeResources() { }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/TupleRenderer.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/jdbm/table/TupleRenderer.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,45 @@
+/*
+ * $Id: TupleRenderer.java,v 1.2 2003/03/13 18:27:35 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.backend.jdbm.table ;
+
+
+import org.apache.eve.backend.BackendException ;
+
+
+/**
+ * A table key/value String renderer for the display or logging of
+ * human readable potentially binary data.
+ * 
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.2 $
+ */
+public interface TupleRenderer
+{
+    /**
+     * Gets the key Object rendered as a String.
+     *
+     * @param a_key the key Object
+     * @return the String representation of the key Object
+     * @throws BackendException if there is a backend failure while trying to
+     * interpret the key.
+     */
+    String getKeyString(Object a_key) ;
+
+    /**
+     * Gets the value Object rendered as a String.
+     *
+     * @param a_value the value Object
+     * @return the String representation of the value Object
+     * @throws BackendException if there is a backend failure while trying to
+     * interpret the value.
+     */
+    String getValueString(Object a_value) ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/package.html
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/backend/package.html	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,4 @@
+
+<p><font color="#999999">$Id: package.html,v 1.1.1.1 2002/11/13 23:46:12 akarasulu Exp $</font> </p>
+<p>The backend package defines interfaces that must be implemented by all Backend 
+  modules. </p>

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientException.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientException.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,38 @@
+/*
+ * $Id: ClientException.java,v 1.2 2003/03/13 18:27:03 akarasulu Exp $
+ * $Prologue$
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.client ;
+
+
+import org.apache.avalon.framework.CascadingRuntimeException ;
+
+
+/** This exception is thrown when protocol errors occurred */
+public class ClientException extends CascadingRuntimeException
+{
+    /**
+     * Constructs an Exception with a detailed message.
+     * @param Message The message associated with the exception.
+     */
+    public ClientException(String message, Throwable t)
+    {
+        super(message, t) ;
+    }
+
+
+    /**
+     * Constructs an Exception with a detailed message.
+     * @param Message The message associated with the exception.
+     */
+    public ClientException(String message)
+    {
+        super(message, null) ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientKey.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientKey.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,286 @@
+/*
+ * $Id: ClientKey.java,v 1.5 2003/08/22 21:15:55 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.client ;
+
+
+import java.net.Socket ;
+import java.net.InetAddress ;
+
+
+/**
+ * Every client that successfully binds anonymously or with a valid identity
+ * has a unique client key represented by this class.  First and foremost the
+ * key is used to uniquely identify the client based on the interface and
+ * port used to connection on the server as well as the interface and port used
+ * by the client.
+ *
+ * The ClientKey plays a central role in coordinating activities with the
+ * server across various threads.  Threads within the same stage or across
+ * stages are synchronized on client resources using lock objects held by a
+ * ClientKey instance.  Socket IO is managed using a pair of lock objects
+ * specificially for this purpose.  As the need arises more lock objects may be
+ * used for specific client resources like a session object.  These extra lock
+ * objects are now being discussed.
+ *
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.5 $
+ */
+public final class ClientKey
+{
+    // ----------------------------------------------
+    // Private members.
+    // ----------------------------------------------
+
+    /** Input channel synchronization object */
+    private final Object m_inputLock = new Object() ;
+    /** Output channel synchronization object */
+    private final Object m_outputLock = new Object() ;
+    /** Server socket interface address */
+    private final InetAddress m_serverAddress ;
+    /** Client socket interface address */
+    private final InetAddress m_clientAddress ;
+	/** Whether or not this key has expired: the client has disconnected. */
+    private boolean m_hasExpired = false ;
+
+    // ----------------------------------------------
+    // pkg-friendly final members: made so the public
+    // accessors do not need to be used by classes in
+    // this pkg - need access to these members for
+    // cleanup operations after expiration.  Do not
+    // want to unnecessarily throw an exception.
+    // ----------------------------------------------
+
+	/** Unique key or client id */
+    final String m_clientId ;
+    /** Server socket TCP port on server host */
+    final int m_serverPort ;
+    /** Client socket TCP port on client host */
+    final int m_clientPort ;
+
+
+    // ----------------------------------------------
+    // Constructors
+    // ----------------------------------------------
+
+
+    /**
+      * Generates a unique connection/client identifier String for a client
+      * socket connection.  The key is composed of the local server address
+      * and port attached to the remote client address and port.  If the
+      * server ip and port are 192.168.1.1:1389 and the client's ip and port are
+      * 34.23.12.1:5678 then the key string would be:
+      *
+      * 192.168.1.1:1389<-34.23.12.1:5678
+      *
+      * This makes the key unique at any single point in time.
+      *
+      * @param a_socket newly established client socket connection to the
+      * server.
+      */
+    ClientKey( final Socket a_socket )
+    {
+        // Extract properties needed to formulate the client id ( key ).
+        m_serverPort = a_socket.getLocalPort() ;
+        m_serverAddress = a_socket.getLocalAddress() ;
+        m_clientPort = a_socket.getPort() ;
+        m_clientAddress = a_socket.getInetAddress() ;
+
+        // Build the key
+        StringBuffer l_buf =
+            new StringBuffer(m_serverAddress.getHostAddress()) ;
+        l_buf.append(':').append(m_serverPort).append("<-") ;
+        l_buf.append(m_clientAddress.getHostAddress()).append(':') ;
+        l_buf.append(m_clientPort) ;
+        m_clientId = l_buf.toString() ;
+    }
+
+
+    // ----------------------------------------------
+    // Accessors of conn. parameters to client id
+    // ----------------------------------------------
+
+
+    /**
+     * Get the unique client id for a connected client based on connection
+     * parameters.
+     *
+     * @return the unique id of the client connection
+     * @throws KeyExpiryException to force the handling of expired keys rather
+     * than depending on developers to maintain a convention of checking for
+     * key expiration before use in other modules.
+     */
+    public String getClientId()
+        throws KeyExpiryException
+    {
+        checkExpiry() ;
+        return m_clientId ;
+    }
+
+
+    /**
+     * Gets the client's IP address.
+     *
+     * @return the client's ip address.
+     * @throws KeyExpiryException to force the handling of expired keys
+     */
+    public String getClientAddress()
+        throws KeyExpiryException
+    {
+        checkExpiry() ;
+		return m_clientAddress.getHostAddress() ;
+    }
+
+
+    /**
+     * Gets the client's hostname.
+     *
+     * @return the client's hostname.
+     * @throws KeyExpiryException to force the handling of expired keys
+     */
+    public String getClientHost()
+        throws KeyExpiryException
+    {
+        checkExpiry() ;
+        return m_clientAddress.getHostName() ;
+    }
+
+
+    // ----------------------------------------------
+    // ClientKey lock object accessors.
+    // ----------------------------------------------
+
+
+    /**
+     * Gets the client's output stream lock object.
+     *
+     * @return ouput lock object.
+     * @throws KeyExpiryException to force the handling of expired keys
+     */
+    public Object getOutputLock()
+        throws KeyExpiryException
+    {
+        checkExpiry() ;
+        return m_outputLock ;
+    }
+
+
+    /**
+     * Gets the client's input stream lock object.
+     *
+     * @return input lock object.
+     * @throws KeyExpiryException to force the handling of expired keys
+     */
+    public Object getInputLock()
+        throws KeyExpiryException
+    {
+        checkExpiry() ;
+        return m_inputLock ;
+    }
+
+
+    // ----------------------------------------------
+    // Key expiration methods.
+    // ----------------------------------------------
+
+
+    /**
+     * Determines if the client represented by this ClientKey is still
+     * connected to the server.  Once disconnected the ClientKey is expired
+     * by the server so processing on behalf of the client does not continue.
+     *
+     * @return true if the client is no longer connected to the server, false
+     * if the client is connected.
+     */
+    public boolean hasExpired()
+    {
+        return m_hasExpired ;
+    }
+
+
+    /**
+     * Expires this key to indicate the disconnection of the client represented
+     * by this key from the server.  It is intentionally package friendly to
+     * only allow access by the ClientModule.
+     */
+    void expire()
+    {
+        m_hasExpired = true ;
+    }
+
+
+    /**
+     * Utility method to throw key expiration exception if this ClientKey has
+     * expired.  This method is called by most accessor methods within this
+     * class with <code>hasExpired()</code> being the only exception.  The
+     * purpose for this is to force ClientKey using modules to check for
+     * expiration rather rely upon them to check to see if the key is valid
+     * before use everytime.
+     * 
+     * @throws KeyExpiryException to force the handling of expired keys rather
+     * than depending on developers to maintain a convention of checking for
+     * key expiration before use in other modules.
+     */
+    private void checkExpiry()
+        throws KeyExpiryException
+    {
+        if(m_hasExpired) {
+            throw new KeyExpiryException() ;
+        }
+    }
+
+
+    // ----------------------------------------------
+    // Class java.lang.Object method overrides.
+    // ----------------------------------------------
+
+
+    /**
+     * For debugging returns the clientId string.
+     *
+     * @return the client id string.
+     */
+    public String toString()
+    {
+        return m_clientId ;
+    }
+
+
+    /**
+     * Gets the hashCode of the unique clientId String.  Overriden to correctly
+     * manage ClientKey's within Map based collections.
+     *
+     * @return the clientId hashCode value.
+     */
+    public int hashCode()
+    {
+        return m_clientId.hashCode() ;
+    }
+
+
+    /**
+     * Determines whether this ClientKey is equivalent to another.  If argument
+     * object is not the same reference the clientId String's are compared using
+     * the <code>String.equal()</code> method.  Required for containment within
+     * collections.
+     *
+     * @return true if an_obj equals this ClientKey, false otherwise.
+     */
+    public boolean equals(Object an_obj)
+    {
+        if(this == an_obj) {
+            return true ;
+        } else if(an_obj instanceof ClientKey) {
+            return ((ClientKey) an_obj).m_clientId.equals(m_clientId) ;
+        }
+
+        return false ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientManager.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientManager.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,123 @@
+/*
+ * $Id: ClientManager.java,v 1.7 2003/08/22 21:15:55 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.client ;
+
+
+import java.net.Socket ;
+import java.io.IOException ;
+import java.io.InputStream ;
+
+import org.apache.eve.event.OutputListener ;
+import org.apache.eve.event.ConnectListener ;
+import org.apache.eve.security.LdapPrincipal ;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * Service interface used to manage clients, their connections and their
+ * stateful sessions.
+ * 
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.7 $
+ */
+public interface ClientManager
+    extends ConnectListener
+{
+    /** role played by this service interface */
+    String ROLE = ClientManager.class.getName() ;
+
+    /**
+     * Drops a client connection and destroys the client's session.
+     *
+     * @param a_clientKey the key used to uniquely identify this client.
+     */
+    void drop( ClientKey a_clientKey ) ;
+
+    /**
+     * Drops a client connection and destroys the client's session associated
+     * with the calling thread.
+     */
+    void drop() ;
+
+    /**
+     * Creates a client session using the client's unique key.
+     * 
+     * @param a_clientKey the client key used to uniquely identify the client.
+     * @param a_socket a client socket to the server.
+     */
+    ClientSession add( ClientKey a_clientKey, Socket a_socket )
+        throws IOException ;
+
+    /**
+     * Sets the user principal for a client on a bind operation.  The protocol
+     * states that a bind operation requires the destruction of all outstanding
+     * operations.  Hence all outstanding operations refered to by the client's
+     * key must be terminated, the session parameters are flushed, then the
+     * princal is set.  The client affectively changes its role without dropping
+     * the socket connection.
+     *
+     * @param a_clientKey the client's unique key.
+     * @param a_principal the new principal to be taken on by the client using
+     * the existing socket connection.
+     */
+    ClientSession
+        setUserPrincipal( ClientKey a_clientKey, LdapPrincipal a_principal ) ;
+
+    /**
+     * Explicit access to a client's session by the client's ClientKey.
+     *
+     * @param a_clientKey the client's unique key
+     * @return the session associated with the client's key.
+     */
+    ClientSession getClientSession( ClientKey a_clientKey ) ;
+
+    /**
+     * Gets the client session associated with the calling thread's context.
+     *
+     * @return the client's session which the current thread is doing work for.
+     */
+    ClientSession getClientSession() ;
+
+    /**
+     * Gets the key of the client on whose behalf the current thread is
+     * executing.
+     *
+     * @return the ClientKey associated with the callers thread or null if none
+     * exists.
+     */
+	ClientKey getClientKey() ;
+
+    /**
+     * Gets the LdapContext associated with the calling thread.
+     *
+     * @return the context of the caller thread
+     */
+    LdapContext getLdapContext() ;
+
+    /**
+     * Associates an external thread using the JNDI provider with a LdapContext.
+     *
+     * @param a_ctx the LdapContext to be associated with the calling thread.
+     */
+    void threadAssociate( LdapContext a_ctx ) ;
+
+    /**
+     * Associates a protocol request handler driving thread with a client.
+     *
+     * @param a_clientKey the unique ClientKey associated with the request.
+     */
+    void threadAssociate( ClientKey a_clientKey ) ;
+
+    /**
+     * Disassociates a protocol request handler driving thread with a client.
+     */
+    void threadDisassociate() ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientManagerSlave.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientManagerSlave.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,15 @@
+/*
+ * $Id: ClientManagerSlave.java,v 1.2 2003/03/13 18:27:04 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.client ;
+
+public interface ClientManagerSlave
+{
+    void registerClientManager(ClientManager a_clientManager) ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientModule.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientModule.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,509 @@
+/*
+ * $Id: ClientModule.java,v 1.13 2003/08/22 21:15:55 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.client ;
+
+
+import java.util.Map ;
+import java.util.HashMap ;
+import java.util.EventObject ;
+
+import java.net.Socket ;
+
+import java.io.IOException ;
+import java.io.InputStream ;
+import java.io.OutputStream ;
+import java.io.PushbackInputStream ;
+import java.io.BufferedOutputStream ;
+
+import org.apache.eve.decoder.Decoder ;
+import org.apache.eve.event.InputEvent ;
+import org.apache.eve.event.OutputEvent ;
+import org.apache.eve.input.InputModule ;
+import org.apache.eve.seda.AbstractStage ;
+import org.apache.eve.event.ConnectEvent ;
+import org.apache.eve.event.EventHandler ;
+import org.apache.eve.input.InputManager ;
+import org.apache.eve.output.OutputModule ;
+import org.apache.eve.output.OutputManager ;
+import org.apache.eve.security.LdapPrincipal ;
+import org.apache.eve.backend.UnifiedBackend ;
+import org.apache.eve.protocol.ProtocolEngine ;
+import org.apache.eve.event.AbstractEventHandler ;
+
+import org.apache.avalon.framework.logger.Logger ;
+import org.apache.avalon.framework.service.ServiceManager ;
+import org.apache.avalon.framework.service.ServiceException ;
+import org.apache.avalon.framework.CascadingRuntimeException ;
+import org.apache.avalon.framework.configuration.Configuration ;
+import org.apache.avalon.cornerstone.services.threads.ThreadManager ;
+import org.apache.avalon.framework.configuration.ConfigurationException ;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * ClientManager service implementation used to manage client sessions and
+ * their socket connections.
+ * 
+ * @phoenix:block
+ * @phoenix:service name="org.apache.eve.client.ClientManager"
+ * @phoenix:mx-topic name="client-manager"
+ * 
+ * @author <a href="mailto:aok123@bellsouth.net">Alex Karasulu</a>
+ * @author $Author: akarasulu $
+ * @version $Revision: 1.13 $
+ */
+public class ClientModule
+    extends AbstractStage
+    implements ClientManager
+{
+    public static final String SOCKETLISTENER_POOL = "client" ;
+
+    private Map m_sockets = new HashMap() ;
+    private Map m_sessions = new HashMap() ;
+	private Decoder m_decoder = null ;
+    private InputManager m_inputManager = null ;
+    private OutputManager m_outputManager = null ;
+    private UnifiedBackend m_nexus = null ;
+    private ProtocolEngine m_engine = null ;
+
+    /**
+     * We are using a ThreadLocal just as a means to key into a session based
+     * on the current thread of execution which has been associated with a
+     * session using the threadAssociate method.  This is done to enable backend
+     * modules to access the session of the user performing operations without
+     * having to pass around the user session.  The ThreadLocal actually maps
+     * the Thread to the ClientKey.  <code>getClientSession()</code> without
+     * arguments simply accesses the ClientKey using the current executing
+     * Thread as the key into this ThreadLocal.  The ClientKey is then used to
+     * return the ClientSession via another lookup into the m_sessions Map.
+     */
+    private ThreadLocal m_threadKeys = new ThreadLocal() ;
+
+    /**
+     * Thread outside of the protocol manager making calling nexus and backend
+     * methods through the JNDI provider need to propagate context environment
+     * parameters without passing is as an argument one each call.  This Thread
+     * Local maps threads to JNDI contexts.
+     */
+    private ThreadLocal m_threadCtxs = new ThreadLocal() ;
+
+
+    // ---------------------------------------------------------
+    // Constructors
+    // ---------------------------------------------------------
+
+
+    /**
+     * Default constructor creates the client module by initializing a
+     * ConnectEventHandler for it.
+     */
+    public ClientModule()
+    {
+        m_handler = new ConnectEventHandler() ;
+    }
+
+
+    // ---------------------------------------------------------
+    // Stage Event Handler Class: ConnectionEventHandler
+    // ---------------------------------------------------------
+
+
+    /**
+     * The stage event handler for the ClientModule.  This class processes
+     * incomming ConnectEvents thrown by the ServerListener service.
+     */
+    class ConnectEventHandler extends AbstractEventHandler
+    {
+        /**
+         * Primary event handling method to process a ConnectEvent.
+         *
+         * @param an_event must be a ConnectEvent subtype of EventObject
+         */
+        public void handleEvent( EventObject an_event )
+        {
+            Socket l_socket = null ;
+            Logger l_log = ClientModule.this.getLogger() ;
+            ClientKey l_clientKey = null ;
+            ConnectEvent l_event = null ;
+
+            try
+            {
+                if( an_event instanceof ConnectEvent )
+                {
+                    l_event = ( ConnectEvent ) an_event ;
+                    l_socket = ( Socket ) l_event.getSource() ;
+                    l_clientKey = new ClientKey( l_socket ) ;
+                    add( l_clientKey, l_socket ) ;
+                }
+                else
+                {
+                    l_log.error( "Unknown event " + an_event + " ignored" ) ;
+                }
+            }
+            catch( Throwable t )
+            {
+                l_log.error( "Droping client " + l_clientKey
+                    + " on handler error: ", t ) ;
+                drop( l_clientKey ) ;
+            }
+        }
+    }
+
+
+    // ---------------------------------------------------------
+    // Add/Drop/Get ClientSession Management Methods
+    // ---------------------------------------------------------
+
+
+    /**
+     * Drops a client connection and destroys the client's session associated
+     * with the calling thread.
+     */
+    public void drop()
+    {
+    	drop( getClientKey() ) ;
+    }
+
+
+    /**
+     * Drops a client connection by unregistering the client with io managers
+     * and closing the client socket connection.
+     *
+     * @param a_clientKey the key of the client to drop
+     */
+    public void drop( ClientKey a_clientKey )
+    {
+        Socket l_socket = null ;
+        ClientSession l_session = null ;
+
+        // Unregister client key with io manager and expire it.
+        m_inputManager.unregister( a_clientKey ) ;
+        m_outputManager.unregister( a_clientKey ) ;
+        a_clientKey.expire() ;
+
+        // Remove client session from session map
+        synchronized( m_sessions )
+        {
+            l_session = ( ClientSession ) m_sessions.remove( a_clientKey ) ;
+        }
+
+        // Invalidate removed session to disable use by held handles to session
+        if( l_session != null )
+        {
+            l_session.invalidate() ;
+        }
+
+        // Remove client socket
+        synchronized( m_sockets )
+        {
+            l_socket = ( Socket ) m_sockets.remove( a_clientKey ) ;
+        }
+
+        // Close the client socket
+        if( null != l_socket )
+        {
+            try
+            {
+	            l_socket.close() ;
+            }
+            catch( IOException e )
+            {
+                String l_msg = "Could not close client " + a_clientKey
+                    + " socket!" ;
+                getLogger().error( l_msg, e ) ;
+                throw new ClientException( l_msg, e ) ;
+            }
+        }
+    }
+
+
+    /**
+     * Adds a client connection by registering the client io streams with the
+     * io managers and tracking the client using the supplied client key.  The
+     * client's session is initialized using an anonymous principal.  Later
+     * bind operations can change the session principal to represent the
+     * appropriate user.
+     *
+     * @param a_clientKey the key of the client to add.
+     * @param a_socket the client's socket.
+     */
+    public ClientSession add( ClientKey a_clientKey, Socket a_socket )
+        throws IOException
+    {
+        LdapClientSession l_session = null ;
+
+        // map client key to socket.
+        synchronized( m_sockets )
+        {
+            m_sockets.put( a_clientKey, a_socket ) ;
+        }
+
+        // Register Socket IO streams with the respective IO manager
+        m_inputManager.register( a_clientKey, a_socket.getInputStream() ) ;
+        m_outputManager.register( a_clientKey, a_socket.getOutputStream() ) ;
+
+        // Create a client session using the default principal (anonymous)
+        // which corresponds to an empty string for the distinguished name.
+        l_session = new LdapClientSession( a_clientKey, new LdapPrincipal() ) ;
+	    m_sessions.put( a_clientKey, l_session ) ;
+        return l_session ;
+    }
+
+
+    /**
+     * Sets the user principal for a client on a bind operation.  The protocol
+     * states that a bind operation requires the destruction of all outstanding
+     * operations.  Hence all outstanding operations refered to by the client's
+     * key must be terminated, the session parameters are flushed, then the
+     * princal is set.  The client affectively changes its role without dropping
+     * the socket connection.
+     *
+     * @todo need to make sure we stop all outstanding request operations before
+     * changing the principal of the user.
+     * @param a_clientKey the client's unique key.
+     * @param a_principal the new principal to be taken on by the client using
+     * the existing socket connection.
+     */
+    public ClientSession
+        setUserPrincipal( ClientKey a_clientKey, LdapPrincipal a_principal )
+    {
+        /** @todo */
+        // Must destroy existing operations here before changing the principal
+        LdapClientSession l_session = ( LdapClientSession )
+            m_sessions.get( a_clientKey ) ;
+
+        // We need to completely replace the session, expire the old key and
+        // create a new one and go from there rather than reseting the session
+        // we want old handles to be useless to both these objects and we
+        // want the code possessing it to be aware when it tries to use it.
+
+        // This is the only way to stop outstanding requests that are in stages
+        // other than those that are in the protocol engine stage.  We basically
+        // need to destroy and recreate everything except the socket connection.
+
+        l_session.reset() ;
+        l_session.setPrincipal( a_principal ) ;
+        return l_session ;
+    }
+
+
+    /**
+     * Gets the ClientSession associated with the ClientKey argument.
+     *
+     * @param a_clientKey the unique client primary key
+     * @return the session of the client
+     */
+    public ClientSession getClientSession( ClientKey a_clientKey )
+    {
+        return ( ClientSession ) m_sessions.get( a_clientKey ) ;
+    }
+
+
+    /**
+     * Gets the key of the client on whose behalf the current thread is
+     * executing.
+     *
+     * @return the ClientKey associated with the callers thread or null if none
+     * exists.
+     */
+	public ClientKey getClientKey()
+    {
+		return ( ClientKey ) m_threadKeys.get() ;
+    }
+
+
+    /**
+     * Gets the ClientKey associated within the context of the calling Thread.
+     *
+     * @see threadAssociate()
+     * @see threadDisassociate()
+     * @return the session of the client associated with the calling Thread
+     */
+    public ClientSession getClientSession()
+    {
+        ClientKey l_key = ( ClientKey ) m_threadKeys.get() ;
+        return ( ClientSession ) m_sessions.get( l_key ) ;
+    }
+
+
+    /**
+     * Gets the LdapContext associated with the calling thread.
+     *
+     * @return the context of the caller thread
+     */
+    public LdapContext getLdapContext()
+    {
+        return ( LdapContext ) m_threadCtxs.get() ;
+    }
+
+
+    /**
+     * Associates the calling thread with a client using a ClientKey.  A call to
+     * this method enables calls to getClientSession() without arguments to
+     * return a non-null ClientSession handle.
+     *
+     * @param a_clientKey the unique client primary key
+     */
+    public void threadAssociate( ClientKey a_clientKey )
+    {
+	    m_threadKeys.set( a_clientKey ) ;
+    }
+
+
+    /**
+     * Associates the calling thread with a JNDI LdapContext.  A call to
+     * this method enables calls to getLdapContext() without arguments to
+     * return an LdapContext handle.
+     *
+     * @param a_ctx the LdapContext to associate with the calling thread
+     */
+    public void threadAssociate( LdapContext a_ctx )
+    {
+	    this.m_threadCtxs.set( a_ctx ) ;
+    }
+
+
+    /**
+     * Disassociates the calling thread with a client.  After a call to this
+     * method the calling thread cannot acquire a non-null handle on a
+     * ClientSession object through a call to getClientSession() without
+     * arguments.
+     */
+    public void threadDisassociate()
+    {
+        if( m_threadKeys.get() != null )
+        {
+        	m_threadKeys.set( null ) ;
+        }
+        else
+        {
+        	m_threadCtxs.set( null ) ;
+        }
+    }
+
+
+    // ---------------------------------------------------------
+    // Listener Implementations
+    // ---------------------------------------------------------
+
+
+    /**
+     * ConnectListener interface implementation which asynchronously processes
+     * the ConnectEvent generated by the ServerListener service.  It merely
+     * enqueues the event onto this Stage's event queue and returns immediately
+     * without processing the event in the thread of the caller.
+     *
+     * @param an_event a client connection event.
+     */
+    public void connectPerformed( ConnectEvent an_event )
+        throws CascadingRuntimeException
+    {
+        enqueue( an_event ) ;
+    }
+
+
+    // ---------------------------------------------------------
+    // Module & Life-Cycle Methods
+    // ---------------------------------------------------------
+
+
+    /**
+     * Overriden to enable handler after enabling this Module via a super
+     * method invokation.
+     *
+     * @param a_logger the logger to set this module and its handler to use
+     */
+    public void enableLogging( Logger a_logger )
+    {
+        super.enableLogging( a_logger ) ;
+        m_handler.enableLogging( a_logger ) ;
+    }
+
+
+    /**
+     * Gets the ROLE of the service this Module implements.
+     *
+     * @phoenix:mx-attribute
+     * @phoenix:mx-description gets the service interface (ROLE)
+     * @phoenix:mx-isWriteable no
+     * @return the role of this service.
+     */
+    public String getImplementationRole()
+    {
+        return ROLE ;
+    }
+
+
+    /**
+     * Gets a descriptive name for this module's implementation.
+     *
+     * @phoenix:mx-attribute
+     * @phoenix:mx-description gets the implementation name
+     * @phoenix:mx-isWriteable no
+     * @return descriptive implementation name.
+     */
+    public String getImplementationName()
+    {
+        return "Client Manager Module" ;
+    }
+
+
+    /**
+     * Gets the fully qualified class name of this service implementation class.
+     *
+     * @phoenix:mx-attribute
+     * @phoenix:mx-description gets the implementation class name
+     * @phoenix:mx-isWriteable no
+     * @return FQCN of this class.
+     */
+    public String getImplementationClassName()
+    {
+        return this.getClass().getName() ;
+    }
+
+
+	/**
+     * Gets a handle on various services minux the ThreadManager which is
+     * accessed by the service method of the AbstractStage superclass.
+     * 
+     * @phoenix:dependency name="org.apache.eve.decoder.Decoder"
+     * @phoenix:dependency name="org.apache.eve.input.InputManager"
+     * @phoenix:dependency name="org.apache.eve.output.OutputManager"
+     * @phoenix:dependency name="org.apache.eve.backend.UnifiedBackend"
+     * @phoenix:dependency name="org.apache.eve.protocol.ProtocolEngine"
+     * @phoenix:dependency name="org.apache.avalon.cornerstone.services.threads.ThreadManager"
+     */
+    public void service( ServiceManager a_manager )
+        throws ServiceException
+    {
+        super.service( a_manager ) ;
+        m_decoder = ( Decoder ) a_manager.lookup( Decoder.ROLE ) ;
+        m_nexus = ( UnifiedBackend ) a_manager.lookup( UnifiedBackend.ROLE ) ;
+        m_engine = ( ProtocolEngine ) a_manager.lookup( ProtocolEngine.ROLE ) ;
+        m_inputManager = ( InputManager )
+            a_manager.lookup( InputManager.ROLE ) ;
+        m_outputManager = ( OutputManager )
+            a_manager.lookup( OutputManager.ROLE ) ;
+    }
+
+
+    /**
+     * Initializes this module by registering it with various slave modules like
+     * the InputManager, OutputManager and ProtocolEngine services.
+     */
+    public void initialize()
+        throws Exception
+    {
+        m_nexus.registerClientManager( this ) ;
+        m_engine.registerClientManager( this ) ;
+        m_inputManager.registerClientManager( this ) ;
+        m_outputManager.registerClientManager( this ) ;
+    }
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientSession.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/client/ClientSession.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,62 @@
+/*
+ * $Id: ClientSession.java,v 1.4 2003/08/22 21:15:55 akarasulu Exp $
+ * $Prologue$
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.client ;
+
+
+import java.util.Locale ;
+import java.util.Iterator;
+
+import org.apache.eve.security.LdapPrincipal ;
+
+
+
+public interface ClientSession
+{
+    ClientKey getClientKey() ;
+
+    ////////////////////////////
+    // Session Attribute APIs //
+    ////////////////////////////
+
+    boolean isValid() ;
+
+    Iterator getAttributeNames() ;
+
+    Object getAttribute(String an_attrName) ;
+
+    void removeAttribute(String an_attrName) ;
+
+    void setAttribute(String an_attrName, Object a_attrValue) ;
+
+    void invalidate() ;
+
+    boolean isNew() ;
+
+    long getCreationTime() ;
+
+    long getLastAccessedTime() ;
+
+    int getMaxInactiveInterval() ;
+
+    void setMaxInactiveInterval(int an_interval) ;
+
+    //ServerConfig getServerConfig() ;
+
+
+    /////////////////////////
+    // Session Client APIs //
+    /////////////////////////
+
+
+    Locale getLocale() ;
+
+    LdapPrincipal getPrincipal() ;
+}

Added: incubator/directory/eve/branches/start/src/java/org/apache/eve/client/KeyExpiryException.java
==============================================================================
--- (empty file)
+++ incubator/directory/eve/branches/start/src/java/org/apache/eve/client/KeyExpiryException.java	Thu Jun 24 00:06:35 2004
@@ -0,0 +1,28 @@
+/*
+ * $Id: KeyExpiryException.java,v 1.1 2003/03/22 19:55:29 akarasulu Exp $
+ *
+ * -- (c) LDAPd Group                                                    --
+ * -- Please refer to the LICENSE.txt file in the root directory of      --
+ * -- any LDAPd project for copyright and distribution information.      --
+ *
+ */
+
+package org.apache.eve.client;
+
+/**
+ * Exception thrown when an accessor method is used on an expired ClientKey
+ */
+public class KeyExpiryException extends Exception {
+    /** Constructs an Exception without a message. */
+    public KeyExpiryException() {
+        super();
+    }
+
+    /**
+     * Constructs an Exception with a detailed message.
+     * @param Message The message associated with the exception.
+     */
+    public KeyExpiryException(String message) {
+        super(message);
+    }
+}

Added: incubator/director