Rename Commons bundles
authorMathieu Baudier <mbaudier@argeo.org>
Wed, 14 Sep 2016 14:16:57 +0000 (14:16 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Wed, 14 Sep 2016 14:16:57 +0000 (14:16 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@9138 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

469 files changed:
dep/org.argeo.dep.cms.client/pom.xml
dep/org.argeo.dep.cms.node/pom.xml
dep/org.argeo.dep.cms.platform/pom.xml
org.argeo.cms.api/.classpath [deleted file]
org.argeo.cms.api/.project [deleted file]
org.argeo.cms.api/bnd.bnd [deleted file]
org.argeo.cms.api/build.properties [deleted file]
org.argeo.cms.api/pom.xml [deleted file]
org.argeo.cms.api/src/org/argeo/node/ArgeoLogListener.java [deleted file]
org.argeo.cms.api/src/org/argeo/node/ArgeoLogger.java [deleted file]
org.argeo.cms.api/src/org/argeo/node/DataAdminPrincipal.java [deleted file]
org.argeo.cms.api/src/org/argeo/node/DataModelNamespace.java [deleted file]
org.argeo.cms.api/src/org/argeo/node/EnumAD.java [deleted file]
org.argeo.cms.api/src/org/argeo/node/EnumOCD.java [deleted file]
org.argeo.cms.api/src/org/argeo/node/NodeConstants.java [deleted file]
org.argeo.cms.api/src/org/argeo/node/NodeDeployment.java [deleted file]
org.argeo.cms.api/src/org/argeo/node/NodeInstance.java [deleted file]
org.argeo.cms.api/src/org/argeo/node/NodeOID.java [deleted file]
org.argeo.cms.api/src/org/argeo/node/NodeState.java [deleted file]
org.argeo.cms.api/src/org/argeo/node/RepoConf.java [deleted file]
org.argeo.cms.api/src/org/argeo/node/node.cnd [deleted file]
org.argeo.cms.api/src/org/argeo/node/package-info.java [deleted file]
org.argeo.cms.api/src/org/argeo/node/packageinfo [deleted file]
org.argeo.cms.ui.workbench.rap/.classpath [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/.project [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/META-INF/spring/commands.xml [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/META-INF/spring/osgi.xml [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle.properties [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle_fr.properties [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle_ru.properties [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/bnd.bnd [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/branding/afterLogout.html [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/branding/empty.html [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/branding/favicon.ico [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/branding/login.html [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/branding/public.html [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/build.properties [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/icons/closeAll.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/icons/exit.png [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/icons/home.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/icons/main.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/icons/password.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/icons/preferences.png [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/plugin.xml [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/pom.xml [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/AnonymousEntryPoint.java [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapActionBarAdvisor.java [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWindowAdvisor.java [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWorkbenchLogin.java [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/SecureRapActivator.java [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/commands/OpenHome.java [new file with mode: 0644]
org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/commands/UserMenu.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/.classpath [new file with mode: 0644]
org.argeo.cms.ui.workbench/.project [new file with mode: 0644]
org.argeo.cms.ui.workbench/META-INF/spring/commands.xml [new file with mode: 0644]
org.argeo.cms.ui.workbench/META-INF/spring/common.xml [new file with mode: 0644]
org.argeo.cms.ui.workbench/META-INF/spring/osgi.xml [new file with mode: 0644]
org.argeo.cms.ui.workbench/META-INF/spring/parts.xml [new file with mode: 0644]
org.argeo.cms.ui.workbench/bnd.bnd [new file with mode: 0644]
org.argeo.cms.ui.workbench/build.properties [new file with mode: 0644]
org.argeo.cms.ui.workbench/icons/add.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench/icons/batch.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench/icons/begin.gif [new file with mode: 0755]
org.argeo.cms.ui.workbench/icons/clear.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench/icons/commit.gif [new file with mode: 0755]
org.argeo.cms.ui.workbench/icons/refresh.png [new file with mode: 0644]
org.argeo.cms.ui.workbench/icons/remove.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench/icons/role.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench/icons/rollback.gif [new file with mode: 0755]
org.argeo.cms.ui.workbench/icons/save.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench/icons/save_security.png [new file with mode: 0644]
org.argeo.cms.ui.workbench/icons/save_security_disabled.png [new file with mode: 0644]
org.argeo.cms.ui.workbench/icons/security.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench/icons/sync.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench/icons/user.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench/icons/users.gif [new file with mode: 0644]
org.argeo.cms.ui.workbench/plugin.xml [new file with mode: 0644]
org.argeo.cms.ui.workbench/pom.xml [new file with mode: 0644]
org.argeo.cms.ui.workbench/security-admin.properties [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminImages.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminPerspective.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminPlugin.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/PartStateChanged.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UiAdminUtils.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UiUserAdminListener.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UserAdminWrapper.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/DeleteGroups.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/DeleteUsers.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/ForceRefresh.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/NewGroup.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/NewUser.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/SaveArgeoUser.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/UserBatchUpdate.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/UserTransactionHandler.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/ArgeoUserEditorInput.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/DefaultUserMainPage.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/GroupMainPage.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/GroupsView.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserEditor.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserEditorInput.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UsersView.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/DomainNameLP.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/MailLP.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/RoleIconLP.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserAdminAbstractLP.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserDragListener.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserNameLP.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserTableDefaultDClickListener.java [new file with mode: 0644]
org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserTransactionProvider.java [new file with mode: 0644]
org.argeo.cms/pom.xml
org.argeo.eclipse.ui.workbench.rap/.project [deleted file]
org.argeo.eclipse.ui.workbench.rap/META-INF/spring/osgi.xml [deleted file]
org.argeo.eclipse.ui.workbench.rap/bnd.bnd [deleted file]
org.argeo.eclipse.ui.workbench.rap/build.properties [deleted file]
org.argeo.eclipse.ui.workbench.rap/pom.xml [deleted file]
org.argeo.eclipse.ui.workbench/pom.xml
org.argeo.eclipse.ui/pom.xml
org.argeo.enterprise/.classpath [new file with mode: 0644]
org.argeo.enterprise/.project [new file with mode: 0644]
org.argeo.enterprise/bnd.bnd [new file with mode: 0644]
org.argeo.enterprise/build.properties [new file with mode: 0644]
org.argeo.enterprise/ext/test/log4j.properties [new file with mode: 0644]
org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/BasicTestConstants.java [new file with mode: 0644]
org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java [new file with mode: 0644]
org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java [new file with mode: 0644]
org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/basic.ldif [new file with mode: 0644]
org.argeo.enterprise/pom.xml [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/DigestUtils.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/DirectoryGroup.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/DirectoryUser.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdapUserAdmin.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifAuthorization.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifGroup.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifName.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUserAdmin.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectory.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectoryException.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/osgi/useradmin/WcXaResource.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/util/naming/AttributesDictionary.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/util/naming/LdifParser.java [new file with mode: 0644]
org.argeo.enterprise/src/org/argeo/util/naming/LdifWriter.java [new file with mode: 0644]
org.argeo.ext.jackrabbit/.classpath [new file with mode: 0644]
org.argeo.ext.jackrabbit/.project [new file with mode: 0644]
org.argeo.ext.jackrabbit/bnd.bnd [new file with mode: 0644]
org.argeo.ext.jackrabbit/build.properties [new file with mode: 0644]
org.argeo.ext.jackrabbit/ext/test/log4j.properties [new file with mode: 0644]
org.argeo.ext.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java [new file with mode: 0644]
org.argeo.ext.jackrabbit/ext/test/org/argeo/security/jackrabbit/repository-memory-test.xml [new file with mode: 0644]
org.argeo.ext.jackrabbit/pom.xml [new file with mode: 0644]
org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessControlProvider.java [new file with mode: 0644]
org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessManager.java [new file with mode: 0644]
org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java [new file with mode: 0644]
org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java [new file with mode: 0644]
org.argeo.ext.rap.ui.workbench/.project [new file with mode: 0644]
org.argeo.ext.rap.ui.workbench/META-INF/spring/osgi.xml [new file with mode: 0644]
org.argeo.ext.rap.ui.workbench/bnd.bnd [new file with mode: 0644]
org.argeo.ext.rap.ui.workbench/build.properties [new file with mode: 0644]
org.argeo.ext.rap.ui.workbench/pom.xml [new file with mode: 0644]
org.argeo.jcr/.classpath [new file with mode: 0644]
org.argeo.jcr/.project [new file with mode: 0644]
org.argeo.jcr/bnd.bnd [new file with mode: 0644]
org.argeo.jcr/build.properties [new file with mode: 0644]
org.argeo.jcr/ext/test/log4j.properties [new file with mode: 0644]
org.argeo.jcr/ext/test/org/argeo/jcr/docbook/DocBookModelTest.java [new file with mode: 0644]
org.argeo.jcr/ext/test/org/argeo/jcr/docbook/WikipediaSample.dbk.xml [new file with mode: 0644]
org.argeo.jcr/ext/test/org/argeo/jcr/docbook/howto.xml [new file with mode: 0644]
org.argeo.jcr/ext/test/org/argeo/jcr/tabular/JcrTabularTest.java [new file with mode: 0644]
org.argeo.jcr/ext/test/org/argeo/server/jcr/JcrResourceAdapterTest.java [new file with mode: 0644]
org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy00.xls [new file with mode: 0644]
org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy01.xls [new file with mode: 0644]
org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy02.xls [new file with mode: 0644]
org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy03.xls [new file with mode: 0644]
org.argeo.jcr/pom.xml [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitAdminLoginModule.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitContainer.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitDataModelMigration.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitWrapper.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/repository-h2.xml [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/repository-localfs.xml [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/repository-memory.xml [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/servlet/OpenInViewSessionProvider.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/servlet/RemotingServlet.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/servlet/WebdavServlet.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/unit/AbstractJackrabbitTestCase.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/unit/jaas.config [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/unit/repository-h2.xml [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jackrabbit/unit/repository-memory.xml [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrException.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrUtils.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/ArgeoNames.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/ArgeoTypes.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/CollectionNodeIterator.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/DefaultJcrListener.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/DefaultRepositoryFactory.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/DefaultRepositoryRegister.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/JcrCallback.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/JcrMonitor.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/JcrRepositoryWrapper.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/JcrResourceAdapter.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/JcrUrlStreamHandler.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/JcrUtils.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/PropertyDiff.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/RepositoryRegister.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/ThreadBoundJcrSessionFactory.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/UserJcrUtils.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/VersionDiff.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/argeo.cnd [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/docbook/DocBookModel.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/docbook/DocBookNames.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/docbook/docbook-full.cnd [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/docbook/docbook.cnd [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileStore.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystem.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/fs/JcrFsException.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/fs/JcrPath.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/proxy/AbstractUrlProxy.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/proxy/ResourceProxy.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/proxy/ResourceProxyServlet.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/security/JcrAuthorizations.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/security/JcrKeyring.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/spring/ThreadBoundSession.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/tabular/JcrTabularRowIterator.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/tabular/JcrTabularWriter.java [new file with mode: 0644]
org.argeo.jcr/src/org/argeo/jcr/unit/AbstractJcrTestCase.java [new file with mode: 0644]
org.argeo.node.api/.classpath [new file with mode: 0644]
org.argeo.node.api/.project [new file with mode: 0644]
org.argeo.node.api/bnd.bnd [new file with mode: 0644]
org.argeo.node.api/build.properties [new file with mode: 0644]
org.argeo.node.api/pom.xml [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/ArgeoLogListener.java [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/ArgeoLogger.java [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/DataAdminPrincipal.java [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/DataModelNamespace.java [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/EnumAD.java [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/EnumOCD.java [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/NodeConstants.java [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/NodeDeployment.java [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/NodeInstance.java [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/NodeOID.java [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/NodeState.java [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/RepoConf.java [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/node.cnd [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/package-info.java [new file with mode: 0644]
org.argeo.node.api/src/org/argeo/node/packageinfo [new file with mode: 0644]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBoot.java
org.argeo.security.core/.classpath [deleted file]
org.argeo.security.core/.project [deleted file]
org.argeo.security.core/bnd.bnd [deleted file]
org.argeo.security.core/build.properties [deleted file]
org.argeo.security.core/ext/test/log4j.properties [deleted file]
org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/BasicTestConstants.java [deleted file]
org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java [deleted file]
org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java [deleted file]
org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/basic.ldif [deleted file]
org.argeo.security.core/pom.xml [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/DigestUtils.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/DirectoryGroup.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/DirectoryUser.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifAuthorization.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifGroup.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifName.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/UserAdminConf.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryException.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java [deleted file]
org.argeo.security.core/src/org/argeo/osgi/useradmin/WcXaResource.java [deleted file]
org.argeo.security.core/src/org/argeo/util/naming/AttributesDictionary.java [deleted file]
org.argeo.security.core/src/org/argeo/util/naming/LdifParser.java [deleted file]
org.argeo.security.core/src/org/argeo/util/naming/LdifWriter.java [deleted file]
org.argeo.security.jackrabbit/.classpath [deleted file]
org.argeo.security.jackrabbit/.project [deleted file]
org.argeo.security.jackrabbit/bnd.bnd [deleted file]
org.argeo.security.jackrabbit/build.properties [deleted file]
org.argeo.security.jackrabbit/ext/test/log4j.properties [deleted file]
org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java [deleted file]
org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/repository-memory-test.xml [deleted file]
org.argeo.security.jackrabbit/pom.xml [deleted file]
org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessControlProvider.java [deleted file]
org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessManager.java [deleted file]
org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java [deleted file]
org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java [deleted file]
org.argeo.security.ui.admin/.classpath [deleted file]
org.argeo.security.ui.admin/.project [deleted file]
org.argeo.security.ui.admin/META-INF/spring/commands.xml [deleted file]
org.argeo.security.ui.admin/META-INF/spring/common.xml [deleted file]
org.argeo.security.ui.admin/META-INF/spring/osgi.xml [deleted file]
org.argeo.security.ui.admin/META-INF/spring/parts.xml [deleted file]
org.argeo.security.ui.admin/bnd.bnd [deleted file]
org.argeo.security.ui.admin/build.properties [deleted file]
org.argeo.security.ui.admin/icons/add.gif [deleted file]
org.argeo.security.ui.admin/icons/batch.gif [deleted file]
org.argeo.security.ui.admin/icons/begin.gif [deleted file]
org.argeo.security.ui.admin/icons/clear.gif [deleted file]
org.argeo.security.ui.admin/icons/commit.gif [deleted file]
org.argeo.security.ui.admin/icons/refresh.png [deleted file]
org.argeo.security.ui.admin/icons/remove.gif [deleted file]
org.argeo.security.ui.admin/icons/role.gif [deleted file]
org.argeo.security.ui.admin/icons/rollback.gif [deleted file]
org.argeo.security.ui.admin/icons/save.gif [deleted file]
org.argeo.security.ui.admin/icons/save_security.png [deleted file]
org.argeo.security.ui.admin/icons/save_security_disabled.png [deleted file]
org.argeo.security.ui.admin/icons/security.gif [deleted file]
org.argeo.security.ui.admin/icons/sync.gif [deleted file]
org.argeo.security.ui.admin/icons/user.gif [deleted file]
org.argeo.security.ui.admin/icons/users.gif [deleted file]
org.argeo.security.ui.admin/plugin.xml [deleted file]
org.argeo.security.ui.admin/pom.xml [deleted file]
org.argeo.security.ui.admin/security-admin.properties [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminImages.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminPerspective.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminPlugin.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/PartStateChanged.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UiAdminUtils.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UiUserAdminListener.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UserAdminWrapper.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/DeleteGroups.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/DeleteUsers.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/ForceRefresh.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewGroup.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewUser.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/SaveArgeoUser.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/UserBatchUpdate.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/UserTransactionHandler.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/ArgeoUserEditorInput.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/DefaultUserMainPage.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/GroupMainPage.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/GroupsView.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserEditor.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserEditorInput.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UsersView.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/DomainNameLP.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/MailLP.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/RoleIconLP.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserAdminAbstractLP.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserDragListener.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserNameLP.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserTableDefaultDClickListener.java [deleted file]
org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserTransactionProvider.java [deleted file]
org.argeo.security.ui.rap/.classpath [deleted file]
org.argeo.security.ui.rap/.project [deleted file]
org.argeo.security.ui.rap/META-INF/spring/commands.xml [deleted file]
org.argeo.security.ui.rap/META-INF/spring/osgi.xml [deleted file]
org.argeo.security.ui.rap/OSGI-INF/l10n/bundle.properties [deleted file]
org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_fr.properties [deleted file]
org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_ru.properties [deleted file]
org.argeo.security.ui.rap/bnd.bnd [deleted file]
org.argeo.security.ui.rap/branding/afterLogout.html [deleted file]
org.argeo.security.ui.rap/branding/empty.html [deleted file]
org.argeo.security.ui.rap/branding/favicon.ico [deleted file]
org.argeo.security.ui.rap/branding/login.html [deleted file]
org.argeo.security.ui.rap/branding/public.html [deleted file]
org.argeo.security.ui.rap/build.properties [deleted file]
org.argeo.security.ui.rap/icons/closeAll.gif [deleted file]
org.argeo.security.ui.rap/icons/exit.png [deleted file]
org.argeo.security.ui.rap/icons/home.gif [deleted file]
org.argeo.security.ui.rap/icons/main.gif [deleted file]
org.argeo.security.ui.rap/icons/password.gif [deleted file]
org.argeo.security.ui.rap/icons/preferences.png [deleted file]
org.argeo.security.ui.rap/plugin.xml [deleted file]
org.argeo.security.ui.rap/pom.xml [deleted file]
org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/AnonymousEntryPoint.java [deleted file]
org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapActionBarAdvisor.java [deleted file]
org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWindowAdvisor.java [deleted file]
org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java [deleted file]
org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWorkbenchLogin.java [deleted file]
org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java [deleted file]
org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureRapActivator.java [deleted file]
org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/commands/OpenHome.java [deleted file]
org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/commands/UserMenu.java [deleted file]
org.argeo.security.ui/pom.xml
org.argeo.server.jcr/.classpath [deleted file]
org.argeo.server.jcr/.project [deleted file]
org.argeo.server.jcr/bnd.bnd [deleted file]
org.argeo.server.jcr/build.properties [deleted file]
org.argeo.server.jcr/ext/test/log4j.properties [deleted file]
org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/DocBookModelTest.java [deleted file]
org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/WikipediaSample.dbk.xml [deleted file]
org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/howto.xml [deleted file]
org.argeo.server.jcr/ext/test/org/argeo/jcr/tabular/JcrTabularTest.java [deleted file]
org.argeo.server.jcr/ext/test/org/argeo/server/jcr/JcrResourceAdapterTest.java [deleted file]
org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy00.xls [deleted file]
org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy01.xls [deleted file]
org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy02.xls [deleted file]
org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy03.xls [deleted file]
org.argeo.server.jcr/pom.xml [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitAdminLoginModule.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitContainer.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitDataModelMigration.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitWrapper.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-h2.xml [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-localfs.xml [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-memory.xml [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/OpenInViewSessionProvider.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/RemotingServlet.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/WebdavServlet.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/AbstractJackrabbitTestCase.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/jaas.config [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/repository-h2.xml [deleted file]
org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/repository-memory.xml [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrException.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrUtils.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/ArgeoNames.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/ArgeoTypes.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/CollectionNodeIterator.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/DefaultJcrListener.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/DefaultRepositoryFactory.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/DefaultRepositoryRegister.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/JcrCallback.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/JcrMonitor.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/JcrRepositoryWrapper.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/JcrResourceAdapter.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/JcrUrlStreamHandler.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/JcrUtils.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/PropertyDiff.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/RepositoryRegister.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/ThreadBoundJcrSessionFactory.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/UserJcrUtils.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/VersionDiff.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/argeo.cnd [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/docbook/DocBookModel.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/docbook/DocBookNames.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/docbook/docbook-full.cnd [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/docbook/docbook.cnd [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileStore.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileSystem.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFsException.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrPath.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/proxy/AbstractUrlProxy.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/proxy/ResourceProxy.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/proxy/ResourceProxyServlet.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/security/JcrAuthorizations.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/security/JcrKeyring.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/spring/ThreadBoundSession.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/tabular/JcrTabularRowIterator.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/tabular/JcrTabularWriter.java [deleted file]
org.argeo.server.jcr/src/org/argeo/jcr/unit/AbstractJcrTestCase.java [deleted file]
pom.xml

index da2dc1160995f0a89a7b0757ad785cf90fc22403..ca8f8adbb518b7510529eef7de54d1324155e45f 100644 (file)
@@ -19,7 +19,7 @@
                </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.server.jcr</artifactId>
+                       <artifactId>org.argeo.jcr</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
 
index 1cf3918b37be91ef920718f5925650b084eb2f1a..1c08089650ccee69ae15a979cc0cb5a516538fab 100644 (file)
@@ -19,7 +19,7 @@
                </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.cms.api</artifactId>
+                       <artifactId>org.argeo.node.api</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
                <dependency>
                </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.security.core</artifactId>
+                       <artifactId>org.argeo.enterprise</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.security.jackrabbit</artifactId>
+                       <artifactId>org.argeo.ext.jackrabbit</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
 
index b450008ed376281a6171dbfb5fc485218c35c2df..91a35419897a6dc2f87ca7275688ed4dcc321c98 100644 (file)
                        <artifactId>org.argeo.dep.cms.node</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.security.core</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
                        <artifactId>org.argeo.security.ui</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.security.ui.admin</artifactId>
+                       <artifactId>org.argeo.cms.ui.workbench</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.eclipse.ui.workbench.rap</artifactId>
+                       <artifactId>org.argeo.ext.rap.ui.workbench</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.security.ui.rap</artifactId>
+                       <artifactId>org.argeo.cms.ui.workbench</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
 
diff --git a/org.argeo.cms.api/.classpath b/org.argeo.cms.api/.classpath
deleted file mode 100644 (file)
index eca7bdb..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
-       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/org.argeo.cms.api/.project b/org.argeo.cms.api/.project
deleted file mode 100644 (file)
index 346a99f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.cms.api</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.pde.PluginNature</nature>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-       </natures>
-</projectDescription>
diff --git a/org.argeo.cms.api/bnd.bnd b/org.argeo.cms.api/bnd.bnd
deleted file mode 100644 (file)
index 52a2bdd..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Provide-Capability: cms.datamodel;name=node;cnd=/org/argeo/node/node.cnd
\ No newline at end of file
diff --git a/org.argeo.cms.api/build.properties b/org.argeo.cms.api/build.properties
deleted file mode 100644 (file)
index 34d2e4d..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-source.. = src/
-output.. = bin/
-bin.includes = META-INF/,\
-               .
diff --git a/org.argeo.cms.api/pom.xml b/org.argeo.cms.api/pom.xml
deleted file mode 100644 (file)
index 12911e6..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-       <modelVersion>4.0.0</modelVersion>
-       <parent>
-               <groupId>org.argeo.commons</groupId>
-               <artifactId>argeo-commons</artifactId>
-               <version>2.1.46-SNAPSHOT</version>
-               <relativePath>..</relativePath>
-       </parent>
-       <artifactId>org.argeo.cms.api</artifactId>
-       <name>Commons CMS API</name>
-       <packaging>jar</packaging>
-       <dependencies>
-       </dependencies>
-</project>
\ No newline at end of file
diff --git a/org.argeo.cms.api/src/org/argeo/node/ArgeoLogListener.java b/org.argeo.cms.api/src/org/argeo/node/ArgeoLogListener.java
deleted file mode 100644 (file)
index 698dfe1..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.node;
-
-/** Framework agnostic interface for log notifications */
-public interface ArgeoLogListener {
-       /**
-        * Appends a log
-        * 
-        * @param username
-        *            authentified user, null for anonymous
-        * @param level
-        *            INFO, DEBUG, WARN, etc. (logging framework specific)
-        * @param category
-        *            hierarchy (logging framework specific)
-        * @param thread
-        *            name of the thread which logged this message
-        * @param msg
-        *            any object as long as its toString() method returns the
-        *            message
-        * @param the
-        *            exception in log4j ThrowableStrRep format
-        */
-       public void appendLog(String username, Long timestamp, String level,
-                       String category, String thread, Object msg, String[] exception);
-}
diff --git a/org.argeo.cms.api/src/org/argeo/node/ArgeoLogger.java b/org.argeo.cms.api/src/org/argeo/node/ArgeoLogger.java
deleted file mode 100644 (file)
index 213286d..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.node;
-
-/**
- * Logging framework agnostic identifying a logging service, to which one can
- * register
- */
-public interface ArgeoLogger {
-       /**
-        * Register for events by threads with the same authentication (or all
-        * threads if admin)
-        */
-       public void register(ArgeoLogListener listener,
-                       Integer numberOfPreviousEvents);
-
-       /**
-        * For admin use only: register for all users
-        * 
-        * @param listener
-        *            the log listener
-        * @param numberOfPreviousEvents
-        *            the number of previous events to notify
-        * @param everything
-        *            if true even anonymous is logged
-        */
-       public void registerForAll(ArgeoLogListener listener,
-                       Integer numberOfPreviousEvents, boolean everything);
-
-       public void unregister(ArgeoLogListener listener);
-
-       public void unregisterForAll(ArgeoLogListener listener);
-}
diff --git a/org.argeo.cms.api/src/org/argeo/node/DataAdminPrincipal.java b/org.argeo.cms.api/src/org/argeo/node/DataAdminPrincipal.java
deleted file mode 100644 (file)
index 743c96f..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.argeo.node;
-
-import java.security.Principal;
-
-/** Allows to modify any data. */
-public final class DataAdminPrincipal implements Principal {
-       // FIXME put auth constants in API
-       private final String name = "OU=node";
-
-       @Override
-       public String getName() {
-               return name;
-       }
-
-       @Override
-       public int hashCode() {
-               return name.hashCode();
-       }
-
-       @Override
-       public boolean equals(Object obj) {
-               return this == obj;
-       }
-
-       @Override
-       public String toString() {
-               return name.toString();
-       }
-
-}
diff --git a/org.argeo.cms.api/src/org/argeo/node/DataModelNamespace.java b/org.argeo.cms.api/src/org/argeo/node/DataModelNamespace.java
deleted file mode 100644 (file)
index 5784196..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.argeo.node;
-
-import org.osgi.resource.Namespace;
-
-/** CMS Data Model capability namespace. */
-public class DataModelNamespace extends Namespace {
-
-       public static final String CMS_DATA_MODEL_NAMESPACE = "cms.datamodel";
-       public static final String CAPABILITY_NAME_ATTRIBUTE = "name";
-       public static final String CAPABILITY_CND_ATTRIBUTE = "cnd";
-
-       private DataModelNamespace() {
-               // empty
-       }
-
-}
diff --git a/org.argeo.cms.api/src/org/argeo/node/EnumAD.java b/org.argeo.cms.api/src/org/argeo/node/EnumAD.java
deleted file mode 100644 (file)
index 1ee6d39..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-package org.argeo.node;
-
-import org.osgi.service.metatype.AttributeDefinition;
-
-interface EnumAD extends AttributeDefinition {
-       String name();
-
-       default Object getDefault() {
-               return null;
-       }
-
-       @Override
-       default String getName() {
-               return name();
-       }
-
-       @Override
-       default String getID() {
-               return getClass().getName() + "." + name();
-       }
-
-       @Override
-       default String getDescription() {
-               return null;
-       }
-
-       @Override
-       default int getCardinality() {
-               return 0;
-       }
-
-       @Override
-       default int getType() {
-               return STRING;
-       }
-
-       @Override
-       default String[] getOptionValues() {
-               return null;
-       }
-
-       @Override
-       default String[] getOptionLabels() {
-               return null;
-       }
-
-       @Override
-       default String validate(String value) {
-               return null;
-       }
-
-       @Override
-       default String[] getDefaultValue() {
-               Object value = getDefault();
-               if (value == null)
-                       return null;
-               return new String[] { value.toString() };
-       }
-}
diff --git a/org.argeo.cms.api/src/org/argeo/node/EnumOCD.java b/org.argeo.cms.api/src/org/argeo/node/EnumOCD.java
deleted file mode 100644 (file)
index daa2f54..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-package org.argeo.node;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
-
-import org.osgi.service.metatype.AttributeDefinition;
-import org.osgi.service.metatype.ObjectClassDefinition;
-
-class EnumOCD<T extends Enum<T>> implements ObjectClassDefinition {
-       private final Class<T> enumClass;
-       private String locale;
-
-       public EnumOCD(Class<T> clazz, String locale) {
-               this.enumClass = clazz;
-               this.locale = locale;
-       }
-
-       @Override
-       public String getName() {
-               return null;
-       }
-
-       @Override
-       public String getID() {
-               return enumClass.getName();
-       }
-
-       @Override
-       public String getDescription() {
-               return null;
-       }
-
-       @Override
-       public AttributeDefinition[] getAttributeDefinitions(int filter) {
-               EnumSet<T> set = EnumSet.allOf(enumClass);
-               List<AttributeDefinition> attrs = new ArrayList<>();
-               for (T key : set)
-                       attrs.add((AttributeDefinition) key);
-               return attrs.toArray(new AttributeDefinition[attrs.size()]);
-       }
-
-       @Override
-       public InputStream getIcon(int size) throws IOException {
-               return null;
-       }
-
-}
diff --git a/org.argeo.cms.api/src/org/argeo/node/NodeConstants.java b/org.argeo.cms.api/src/org/argeo/node/NodeConstants.java
deleted file mode 100644 (file)
index 477b0b9..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-package org.argeo.node;
-
-public interface NodeConstants {
-       /*
-        * PIDs
-        */
-       String NODE_STATE_PID = "org.argeo.node.state";
-       String NODE_DEPLOYMENT_PID = "org.argeo.node.deployment";
-       String NODE_INSTANCE_PID = "org.argeo.node.instance";
-
-//     String NODE_REPO_PID = "org.argeo.node.repo";
-       String NODE_USER_ADMIN_PID = "org.argeo.node.userAdmin";
-
-       /*
-        * FACTORY PIDs
-        */
-       String NODE_REPOS_FACTORY_PID = "org.argeo.node.repos";
-       String NODE_USER_DIRECTORIES_FACTORY_PID = "org.argeo.node.userDirectories";
-
-       /*
-        * DEPLOY
-        */
-       String DEPLOY_BASEDN = "ou=deploy,ou=node";
-
-       /*
-        * FRAMEWORK PROPERTIES
-        */
-       String NODE_INIT = "argeo.node.init";
-       String I18N_DEFAULT_LOCALE = "argeo.i18n.defaultLocale";
-       String I18N_LOCALES = "argeo.i18n.locales";
-       // Node Security
-       String ROLES_URI = "argeo.node.roles.uri";
-       /** URI to an LDIF file or LDAP server used as initialization or backend */
-       String USERADMIN_URIS = "argeo.node.useradmin.uris";
-       // Node
-       /** Properties configuring the node repository */
-       String NODE_REPO_PROP_PREFIX = "argeo.node.repo.";
-
-       /*
-        * STANDARD ATTRIBUTES
-        */
-       String CN = "cn";
-       String OU = "ou";
-       String LABELED_URI = "labeledUri";
-       
-       /*
-        * STANDARD VALUES
-        */
-       String DEFAULT = "default";
-}
diff --git a/org.argeo.cms.api/src/org/argeo/node/NodeDeployment.java b/org.argeo.cms.api/src/org/argeo/node/NodeDeployment.java
deleted file mode 100644 (file)
index 8e5558d..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.argeo.node;
-
-public interface NodeDeployment {
-       Long getAvailableSince();
-}
diff --git a/org.argeo.cms.api/src/org/argeo/node/NodeInstance.java b/org.argeo.cms.api/src/org/argeo/node/NodeInstance.java
deleted file mode 100644 (file)
index e1e4bcd..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.argeo.node;
-
-public interface NodeInstance {
-
-}
diff --git a/org.argeo.cms.api/src/org/argeo/node/NodeOID.java b/org.argeo.cms.api/src/org/argeo/node/NodeOID.java
deleted file mode 100644 (file)
index 9d8ff3d..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.argeo.node;
-
-interface NodeOID {
-       String BASE = "1.3.6.1.4.1" + ".48308" + ".1";
-
-       // ATTRIBUTE TYPES
-       String ATTRIBUTE_TYPES = BASE + ".4";
-       String URI = ATTRIBUTE_TYPES + ".1";
-       String HTTP_PORT = ATTRIBUTE_TYPES + ".2";
-       String HTTPS_PORT = ATTRIBUTE_TYPES + ".3";
-
-       // OBJECT CLASSES
-       String OBJECT_CLASSES = BASE + ".6";
-       String JCR_REPOSITORY = OBJECT_CLASSES + ".1";
-
-       // EXTERNAL
-       String LABELED_URI = "1.3.6.1.4.1.250.1.57";
-}
diff --git a/org.argeo.cms.api/src/org/argeo/node/NodeState.java b/org.argeo.cms.api/src/org/argeo/node/NodeState.java
deleted file mode 100644 (file)
index d7148c6..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.argeo.node;
-
-import java.util.List;
-import java.util.Locale;
-
-public interface NodeState {
-       Locale getDefaultLocale();
-
-       List<Locale> getLocales();
-
-       String getHostname();
-
-       boolean isClean();
-       
-       Long getAvailableSince();
-
-}
diff --git a/org.argeo.cms.api/src/org/argeo/node/RepoConf.java b/org.argeo.cms.api/src/org/argeo/node/RepoConf.java
deleted file mode 100644 (file)
index be4f6f7..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.argeo.node;
-
-/** JCR repository configuration */
-public enum RepoConf implements EnumAD {
-       /** Repository type */
-       type("localfs"),
-       /** Default workspace */
-       @Deprecated
-       defaultWorkspace("main"),
-       /** Database URL */
-       dburl(null),
-       /** Database user */
-       dbuser(null),
-       /** Database password */
-       dbpassword(null),
-
-       /** The identifier (can be an URL locating the repo) */
-       labeledUri(null),
-       //
-       // JACKRABBIT SPECIFIC
-       //
-       /** Maximum database pool size */
-       maxPoolSize(10),
-       /** Maximum cache size in MB */
-       @Deprecated
-       maxCacheMB(null),
-       /** Bundle cache size in MB */
-       bundleCacheMB(8),
-       /** Extractor pool size */
-       extractorPoolSize(0),
-       /** Search cache size */
-       searchCacheSize(1000),
-       /** Max volatile index size */
-       maxVolatileIndexSize(1048576);
-
-       /** The default value. */
-       private Object def;
-       private String oid;
-
-       RepoConf(String oid, Object def) {
-               this.oid = oid;
-               this.def = def;
-       }
-
-       RepoConf(Object def) {
-               this.def = def;
-       }
-
-       public Object getDefault() {
-               return def;
-       }
-
-       @Override
-       public String getID() {
-               if (oid != null)
-                       return oid;
-               return EnumAD.super.getID();
-       }
-
-       public static class OCD extends EnumOCD<RepoConf> {
-               public OCD(String locale) {
-                       super(RepoConf.class, locale);
-               }
-       }
-
-}
diff --git a/org.argeo.cms.api/src/org/argeo/node/node.cnd b/org.argeo.cms.api/src/org/argeo/node/node.cnd
deleted file mode 100644 (file)
index fbfea9d..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<argeo = 'http://www.argeo.org/ns/argeo'>
-
-// GENERIC TYPES NOT AVAILABLE IN JCR
-[argeo:link] > mix:created, mix:lastModified
-mixin
-// URI(s)
-- argeo:uri (STRING) m
-
-[argeo:references] > nt:unstructured
-- * (REFERENCE) *
-
-// DATA MODEL
-[argeo:dataModel] > mix:created, mix:lastModified, mix:versionable
-mixin
-- argeo:uri (STRING) m
-- argeo:dataModelVersion (STRING) m
-
-// USER NODES
-// user should be lower case, between 3 and 15 characters long
-[argeo:userHome] > mix:created, mix:lastModified
-mixin
-- argeo:userID (STRING) m
-- argeo:remoteRoles (STRING) *
-// deprecated. for backward compatibility:
-+ argeo:profile (argeo:userProfile)
-+ argeo:keyring (argeo:pbeSpec)
-+ argeo:preferences (argeo:preferenceNode)
-
-[argeo:userProfile] > mix:created, mix:lastModified, mix:title, mix:versionable
-mixin
-- argeo:userID (STRING) m
-- argeo:enabled (BOOLEAN)
-- argeo:accountNonExpired (BOOLEAN)
-- argeo:accountNonLocked (BOOLEAN)
-- argeo:credentialsNonExpired (BOOLEAN)
-
-[argeo:preferenceNode] > mix:lastModified, mix:versionable
-mixin
-+ * (argeo:preferenceNode) * version
-
-[argeo:remoteRepository] > nt:unstructured
-- argeo:uri (STRING)
-- argeo:userID (STRING)
-+ argeo:password (argeo:encrypted)
-
-// TABULAR CONTENT
-[argeo:table] > nt:file
-+ * (argeo:column) *
-
-[argeo:column] > mix:title
-- jcr:requiredType (STRING) = 'STRING'
-
-[argeo:csv] > nt:resource
-
-// CRYPTO
-[argeo:encrypted] > nt:base
-mixin
-// initialization vector used by some algorithms
-- argeo:iv (BINARY)
-
-[argeo:pbeKeySpec] > nt:base
-mixin
-- argeo:secretKeyFactory (STRING)
-- argeo:salt (BINARY)
-- argeo:iterationCount (LONG)
-- argeo:keyLength (LONG)
-- argeo:secretKeyEncryption (STRING)
-
-[argeo:pbeSpec] > argeo:pbeKeySpec
-mixin
-- argeo:cipher (STRING)
-
diff --git a/org.argeo.cms.api/src/org/argeo/node/package-info.java b/org.argeo.cms.api/src/org/argeo/node/package-info.java
deleted file mode 100644 (file)
index fda3bae..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-/**
- * Abstractions or constants related to an Argeo Node, an active repository of
- * linked data.
- */
-package org.argeo.node;
\ No newline at end of file
diff --git a/org.argeo.cms.api/src/org/argeo/node/packageinfo b/org.argeo.cms.api/src/org/argeo/node/packageinfo
deleted file mode 100644 (file)
index 2c9afe8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-version 2.1.0
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench.rap/.classpath b/org.argeo.cms.ui.workbench.rap/.classpath
new file mode 100644 (file)
index 0000000..457b115
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src" />
+       <classpathentry kind="con"
+               path="org.eclipse.pde.core.requiredPlugins" />
+       <classpathentry kind="con"
+               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" />
+       <classpathentry kind="output" path="bin" />
+</classpath>
diff --git a/org.argeo.cms.ui.workbench.rap/.project b/org.argeo.cms.ui.workbench.rap/.project
new file mode 100644 (file)
index 0000000..49b4b90
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.cms.ui.workbench.rap</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/org.argeo.cms.ui.workbench.rap/META-INF/spring/commands.xml b/org.argeo.cms.ui.workbench.rap/META-INF/spring/commands.xml
new file mode 100644 (file)
index 0000000..802d31d
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+       <bean id="openChangePasswordDialog" class="org.argeo.security.ui.commands.OpenChangePasswordDialog"
+               scope="prototype">
+               <property name="userAdmin" ref="userAdmin" />
+               <property name="userTransaction" ref="userTransaction" />
+       </bean>
+
+
+       <!-- RAP Specific command and corresponding service to enable open file -->
+       <bean id="org.argeo.eclipse.ui.workbench.openFile" class="org.argeo.eclipse.ui.workbench.commands.OpenFile"
+       scope="prototype">
+               <property name="openFileServiceId"
+                       value="org.argeo.security.ui.specific.openFileService" />
+       </bean>
+       <!-- Useless - nothing to inject -->
+       <!-- <bean id="org.argeo.security.ui.specific.openFileService" class="org.argeo.eclipse.ui.specific.OpenFileService" 
+               scope="prototype"> </bean> -->
+</beans>
diff --git a/org.argeo.cms.ui.workbench.rap/META-INF/spring/osgi.xml b/org.argeo.cms.ui.workbench.rap/META-INF/spring/osgi.xml
new file mode 100644 (file)
index 0000000..84e5d7b
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
+       xmlns:osgi="http://www.springframework.org/schema/osgi"\r
+       xsi:schemaLocation="http://www.springframework.org/schema/osgi  \r
+       http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
+       http://www.springframework.org/schema/beans   \r
+       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"\r
+       osgi:default-timeout="30000">\r
+\r
+       <reference id="userAdmin" interface="org.osgi.service.useradmin.UserAdmin" />\r
+       <reference id="userTransaction" interface="javax.transaction.UserTransaction" />\r
+</beans:beans>
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle.properties b/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle.properties
new file mode 100644 (file)
index 0000000..4dff7af
--- /dev/null
@@ -0,0 +1 @@
+changePassword=Change password
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle_fr.properties b/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle_fr.properties
new file mode 100644 (file)
index 0000000..158d6fa
--- /dev/null
@@ -0,0 +1 @@
+changePassword=Changer de mot de passe
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle_ru.properties b/org.argeo.cms.ui.workbench.rap/OSGI-INF/l10n/bundle_ru.properties
new file mode 100644 (file)
index 0000000..11dd100
--- /dev/null
@@ -0,0 +1 @@
+changePassword=\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C
diff --git a/org.argeo.cms.ui.workbench.rap/bnd.bnd b/org.argeo.cms.ui.workbench.rap/bnd.bnd
new file mode 100644 (file)
index 0000000..31a9917
--- /dev/null
@@ -0,0 +1,12 @@
+Bundle-SymbolicName: org.argeo.cms.ui.workbench.rap;singleton:=true
+Bundle-Activator: org.argeo.security.ui.rap.SecureRapActivator
+Bundle-ActivationPolicy: lazy
+Require-Bundle: org.eclipse.rap.ui,org.eclipse.core.runtime
+
+Import-Package: org.argeo.eclipse.spring,\
+org.argeo.eclipse.ui.specific,\
+org.argeo.eclipse.ui.workbench.commands,\
+org.argeo.cms,\
+org.argeo.cms.auth,\
+org.argeo.security.ui,\
+*
diff --git a/org.argeo.cms.ui.workbench.rap/branding/afterLogout.html b/org.argeo.cms.ui.workbench.rap/branding/afterLogout.html
new file mode 100644 (file)
index 0000000..ae0901b
--- /dev/null
@@ -0,0 +1,18 @@
+<html>
+<head></head>
+<body>
+<center>
+<table height="100%">
+<tr>
+       <td style="vertical-align:middle">
+               <a 
+                       style="font-family:sans-serif;color:#0066CC;text-decoration:none;" 
+                       href="node" 
+                       title="Click to log in"
+               >Login...</a>
+       </td>
+</tr>
+</table>
+</center>
+</body>
+</html>
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench.rap/branding/empty.html b/org.argeo.cms.ui.workbench.rap/branding/empty.html
new file mode 100644 (file)
index 0000000..94fe28a
--- /dev/null
@@ -0,0 +1,5 @@
+<html>
+<head></head>
+<body>
+</body>
+</html>
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench.rap/branding/favicon.ico b/org.argeo.cms.ui.workbench.rap/branding/favicon.ico
new file mode 100644 (file)
index 0000000..213cdf7
Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/branding/favicon.ico differ
diff --git a/org.argeo.cms.ui.workbench.rap/branding/login.html b/org.argeo.cms.ui.workbench.rap/branding/login.html
new file mode 100644 (file)
index 0000000..6de7eb2
--- /dev/null
@@ -0,0 +1,18 @@
+<html>
+<head></head>
+<body>
+<center>
+<table height="100%">
+<tr>
+       <td style="vertical-align:middle">
+               <a 
+                       style="font-family:sans-serif;color:#0066CC;text-decoration:none;" 
+                       href="javascript:location.reload(true);" 
+                       title="Click to log in"
+               >Login...</a>
+       </td>
+</tr>
+</table>
+</center>
+</body>
+</html>
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench.rap/branding/public.html b/org.argeo.cms.ui.workbench.rap/branding/public.html
new file mode 100644 (file)
index 0000000..e50f6e9
--- /dev/null
@@ -0,0 +1,18 @@
+<html>
+<head></head>
+<body>
+<center>
+<table height="100%">
+<tr>
+       <td style="vertical-align:middle">
+               <a 
+                       style="font-family:sans-serif;color:#0066CC;text-decoration:none;" 
+                       href="javascript:location.reload(true);" 
+                       title="Refresh"
+               >Refresh...</a>
+       </td>
+</tr>
+</table>
+</center>
+</body>
+</html>
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench.rap/build.properties b/org.argeo.cms.ui.workbench.rap/build.properties
new file mode 100644 (file)
index 0000000..ae37429
--- /dev/null
@@ -0,0 +1,2 @@
+source.. = src/
+bin.includes = OSGI-INF/
diff --git a/org.argeo.cms.ui.workbench.rap/icons/closeAll.gif b/org.argeo.cms.ui.workbench.rap/icons/closeAll.gif
new file mode 100644 (file)
index 0000000..28a3785
Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/icons/closeAll.gif differ
diff --git a/org.argeo.cms.ui.workbench.rap/icons/exit.png b/org.argeo.cms.ui.workbench.rap/icons/exit.png
new file mode 100644 (file)
index 0000000..cfbf9d1
Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/icons/exit.png differ
diff --git a/org.argeo.cms.ui.workbench.rap/icons/home.gif b/org.argeo.cms.ui.workbench.rap/icons/home.gif
new file mode 100644 (file)
index 0000000..fd0c669
Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/icons/home.gif differ
diff --git a/org.argeo.cms.ui.workbench.rap/icons/main.gif b/org.argeo.cms.ui.workbench.rap/icons/main.gif
new file mode 100644 (file)
index 0000000..90a0014
Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/icons/main.gif differ
diff --git a/org.argeo.cms.ui.workbench.rap/icons/password.gif b/org.argeo.cms.ui.workbench.rap/icons/password.gif
new file mode 100644 (file)
index 0000000..a6b251f
Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/icons/password.gif differ
diff --git a/org.argeo.cms.ui.workbench.rap/icons/preferences.png b/org.argeo.cms.ui.workbench.rap/icons/preferences.png
new file mode 100644 (file)
index 0000000..aa0dc0b
Binary files /dev/null and b/org.argeo.cms.ui.workbench.rap/icons/preferences.png differ
diff --git a/org.argeo.cms.ui.workbench.rap/plugin.xml b/org.argeo.cms.ui.workbench.rap/plugin.xml
new file mode 100644 (file)
index 0000000..243a433
--- /dev/null
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+   <extension
+         point="org.eclipse.rap.ui.entrypoint">
+      <entrypoint
+            id="org.argeo.security.ui.rap.secureEntryPoint"
+            class="org.argeo.security.ui.rap.RapWorkbenchLogin"
+            path="/node"
+            brandingId="org.argeo.security.ui.rap.defaultBranding">
+      </entrypoint>
+      <entrypoint
+            id="org.argeo.security.ui.rap.anonymousEntryPoint"
+            class="org.argeo.security.ui.rap.AnonymousEntryPoint"
+            path="/public"
+            brandingId="org.argeo.security.ui.rap.defaultBranding">
+      </entrypoint>
+      <entrypoint
+            brandingId="org.argeo.security.ui.rap.defaultBranding"
+            class="org.argeo.security.ui.rap.RapWorkbenchLogin"
+            id="org.argeo.security.ui.rap.secureEntryPoint"
+            path="/login">
+      </entrypoint>
+   </extension>
+
+       <!-- COMMANDS --> 
+       <extension point="org.eclipse.ui.commands">
+               <command
+                       id="org.argeo.security.ui.rap.mainMenuCommand"
+                       defaultHandler="org.argeo.security.ui.rap.commands.OpenHome"
+                       name="Main"> 
+               </command>
+               <command
+                       id="org.argeo.security.ui.rap.openChangePasswordDialog"
+                       defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+                       name="%changePassword">
+               </command>
+               <!-- Enable an "open file" action in a single sourced application  -->  
+               <command
+                       defaultHandler="org.argeo.eclipse.spring.SpringExtensionFactory"
+                       id="org.argeo.eclipse.ui.workbench.openFile"
+                       name="OpenFile">
+                       <commandParameter
+                       id="param.fileName"
+                       name="The name of the file to open (optional)">
+                       </commandParameter>
+            <commandParameter
+                       id="param.fileURI"
+                       name="The URI of this file on the server">
+                       </commandParameter>
+                       <commandParameter
+                       id="param.filePath"
+                       name="The absolute path of this file on the server file system">
+                       </commandParameter>
+               </command>
+       </extension>
+
+       <!-- MENUS --> 
+       <extension point="org.eclipse.ui.menus">
+       <!-- Main tool bar menu -->
+       <menuContribution locationURI="toolbar:org.eclipse.ui.main.toolbar">
+               <toolbar id="org.argeo.security.ui.rap.userToolbar">
+                               <command
+                                       commandId="org.argeo.security.ui.rap.mainMenuCommand"
+                                       icon="icons/home.gif"
+                                       id="org.argeo.security.ui.rap.mainMenu"
+                                       style="pulldown">
+                               </command>
+                               <command commandId="org.eclipse.ui.file.save"/>
+                               <command commandId="org.eclipse.ui.file.saveAll"/>
+                       </toolbar>
+               </menuContribution>
+               
+               <!-- User drop down default menu -->
+               <menuContribution locationURI="menu:org.argeo.security.ui.rap.mainMenu">
+                       <!-- Managed programmatically in the RapActionBarAdvisor to enable 
+                            the display of the current logged-in user id -->
+                       <command
+                               commandId="org.argeo.security.ui.rap.userMenuCommand"
+                               icon="icons/main.gif"
+                               id="org.argeo.security.ui.rap.userMenu">
+                       </command>
+                       <!-- Still unused
+                       <command
+                               commandId="org.eclipse.ui.window.preferences"
+                               icon="icons/preferences.png"/> -->
+               <command
+                               commandId="org.argeo.security.ui.rap.openChangePasswordDialog"
+                               icon="icons/password.gif"
+                               label="%changePassword"/>
+                       <separator
+                               name="org.argeo.security.ui.rap.beforeFile"
+                               visible="true">
+                       </separator>
+                       <command
+                               commandId="org.eclipse.ui.file.closeAll"
+                               icon="icons/closeAll.gif"/>
+                       <command commandId="org.eclipse.ui.file.save"/>
+                       <command commandId="org.eclipse.ui.file.saveAll"/>
+       
+                       <!--<command commandId="org.eclipse.ui.views.showView"/>-->
+               <!--<command commandId="org.eclipse.ui.perspectives.showPerspective"/>-->
+       
+                       <separator
+                               name="org.argeo.security.ui.rap.beforeExit"
+                               visible="true">
+                       </separator>
+                       <command commandId="org.eclipse.ui.file.exit" icon="icons/exit.png"/>
+               </menuContribution>
+       </extension>
+               
+    <!-- SERVICE HANDLERS --> 
+       <extension point="org.eclipse.rap.ui.serviceHandler">
+               <!-- Rap specific service handler to enable file download over the internet-->
+               <serviceHandler
+                       class="org.argeo.eclipse.ui.specific.OpenFileService"
+                       id="org.argeo.security.ui.specific.openFileService">
+               </serviceHandler>
+       </extension>
+    
+    <!-- ACTIVITIES -->
+       <extension
+           point="org.eclipse.ui.activities">
+        <activity
+              description="Anonymous"
+              id="org.argeo.security.ui.rap.anonymousActivity"
+              name="Anonymous">
+                 <enabledWhen>
+                   <with variable="roles">
+                     <iterate ifEmpty="false" operator="or">
+                       <equals value="cn=anonymous,ou=roles,ou=node" />
+                     </iterate>
+                   </with>
+                 </enabledWhen>
+        </activity>
+        <activity
+              description="Not anonymous"
+              id="org.argeo.security.ui.rap.notAnonymousActivity"
+              name="NotAnonymous">
+                 <enabledWhen>
+                       <not>
+                   <with variable="roles">
+                     <iterate ifEmpty="false" operator="or">
+                       <equals value="cn=anonymous,ou=roles,ou=node" />
+                     </iterate>
+                   </with>
+                   </not>
+                 </enabledWhen>
+        </activity>
+               <activityPatternBinding
+              activityId="org.argeo.security.ui.rap.notAnonymousActivity"
+              pattern="org.argeo.security.ui.rap/org.argeo.security.ui.rap.userMenuCommand"/>         
+        <activityPatternBinding
+              activityId="org.argeo.security.ui.rap.notAnonymousActivity"
+              pattern="org.argeo.security.ui.rap/org.eclipse.ui.window.preferences"/>
+        <activityPatternBinding
+              activityId="org.argeo.security.ui.rap.notAnonymousActivity"
+              pattern="org.argeo.security.ui.rap/org.argeo.security.ui.rap.openChangePasswordDialog"/>
+     </extension>
+    
+    <!-- BRANDINGS --> 
+     <extension
+         point="org.eclipse.rap.ui.branding">
+       <branding
+                       id="org.argeo.security.ui.rap.defaultBranding"
+            themeId="org.eclipse.rap.rwt.theme.Default"
+            title="Argeo Web UI"
+            favicon="branding/favicon.ico">
+       </branding>
+       <!-- we need a servlet with this name j_spring_security_logout
+                for the logout filter -->
+       <branding
+                       id="org.argeo.security.ui.rap.logoutBranding"
+            title="Argeo Logout"
+            favicon="branding/favicon.ico"
+            body="branding/empty.html">
+       </branding>
+       </extension>
+</plugin>
diff --git a/org.argeo.cms.ui.workbench.rap/pom.xml b/org.argeo.cms.ui.workbench.rap/pom.xml
new file mode 100644 (file)
index 0000000..2822051
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <version>2.1.46-SNAPSHOT</version>
+               <artifactId>argeo-commons</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.cms.ui.workbench.rap</artifactId>
+       <name>CMS Workbench RAP</name>
+       <packaging>jar</packaging>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.security.ui</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.eclipse.ui.rap</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.cms</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/AnonymousEntryPoint.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/AnonymousEntryPoint.java
new file mode 100644 (file)
index 0000000..04b6f0a
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.rap;
+
+import java.security.PrivilegedAction;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.auth.AuthConstants;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.application.EntryPoint;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * RAP entry point which authenticates the subject as anonymous, for public
+ * unauthenticated access.
+ */
+public class AnonymousEntryPoint implements EntryPoint {
+       private final static Log log = LogFactory.getLog(AnonymousEntryPoint.class);
+
+       /**
+        * How many seconds to wait before invalidating the session if the user has
+        * not yet logged in.
+        */
+       private Integer sessionTimeout = 5 * 60;
+
+       @Override
+       public int createUI() {
+               RWT.getRequest().getSession().setMaxInactiveInterval(sessionTimeout);
+
+               // if (log.isDebugEnabled())
+               // log.debug("Anonymous THREAD=" + Thread.currentThread().getId()
+               // + ", sessionStore=" + RWT.getSessionStore().getId());
+
+               final Display display = PlatformUI.createDisplay();
+               Subject subject = new Subject();
+
+               final LoginContext loginContext;
+               try {
+                       loginContext = new LoginContext(AuthConstants.LOGIN_CONTEXT_ANONYMOUS,
+                                       subject);
+                       loginContext.login();
+               } catch (LoginException e1) {
+                       throw new CmsException("Cannot initialize login context", e1);
+               }
+
+               // identify after successful login
+               if (log.isDebugEnabled())
+                       log.debug("Authenticated " + subject);
+               final String username = subject.getPrincipals().iterator().next()
+                               .getName();
+
+               // Logout callback when the display is disposed
+               display.disposeExec(new Runnable() {
+                       public void run() {
+                               log.debug("Display disposed");
+                               logout(loginContext, username);
+                       }
+               });
+
+               //
+               // RUN THE WORKBENCH
+               //
+               Integer returnCode = null;
+               try {
+                       returnCode = Subject.doAs(subject, new PrivilegedAction<Integer>() {
+                               public Integer run() {
+                                       RapWorkbenchAdvisor workbenchAdvisor = new RapWorkbenchAdvisor(
+                                                       null);
+                                       int result = PlatformUI.createAndRunWorkbench(display,
+                                                       workbenchAdvisor);
+                                       return new Integer(result);
+                               }
+                       });
+                       logout(loginContext, username);
+                       if (log.isTraceEnabled())
+                               log.trace("Return code " + returnCode);
+               } finally {
+                       display.dispose();
+               }
+               return 1;
+       }
+
+       private void logout(LoginContext loginContext, String username) {
+               try {
+                       loginContext.logout();
+                       log.info("Logged out " + (username != null ? username : "")
+                                       + " (THREAD=" + Thread.currentThread().getId() + ")");
+               } catch (LoginException e) {
+                       log.error("Erorr when logging out", e);
+               }
+       }
+}
diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapActionBarAdvisor.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapActionBarAdvisor.java
new file mode 100644 (file)
index 0000000..14bcee1
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.rap;
+
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.security.ui.commands.OpenHomePerspective;
+import org.eclipse.core.commands.Category;
+import org.eclipse.core.commands.Command;
+import org.eclipse.jface.action.ICoolBarManager;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
+import org.eclipse.ui.application.ActionBarAdvisor;
+import org.eclipse.ui.application.IActionBarConfigurer;
+import org.eclipse.ui.commands.ICommandService;
+
+/** Eclipse rap specific action bar advisor */
+public class RapActionBarAdvisor extends ActionBarAdvisor {
+       private final static String ID_BASE = "org.argeo.security.ui.rap";
+       // private final static Log log = LogFactory
+       // .getLog(SecureActionBarAdvisor.class);
+
+       /** Null means anonymous */
+       private String username = null;
+
+       // private IAction logoutAction;
+       // private IWorkbenchAction openPerspectiveDialogAction;
+       // private IWorkbenchAction showViewMenuAction;
+       // private IWorkbenchAction preferences;
+       private IWorkbenchAction saveAction;
+       private IWorkbenchAction saveAllAction;
+
+       // private IWorkbenchAction closeAllAction;
+
+       public RapActionBarAdvisor(IActionBarConfigurer configurer, String username) {
+               super(configurer);
+               this.username = username;
+       }
+
+       protected void makeActions(IWorkbenchWindow window) {
+               // preferences = ActionFactory.PREFERENCES.create(window);
+               // register(preferences);
+               // openPerspectiveDialogAction = ActionFactory.OPEN_PERSPECTIVE_DIALOG
+               // .create(window);
+               // register(openPerspectiveDialogAction);
+               // showViewMenuAction = ActionFactory.SHOW_VIEW_MENU.create(window);
+               // register(showViewMenuAction);
+               //
+               // // logout
+               // logoutAction = ActionFactory.QUIT.create(window);
+               // // logoutAction = createLogoutAction();
+               // register(logoutAction);
+               //
+               // Save semantics
+               saveAction = ActionFactory.SAVE.create(window);
+               register(saveAction);
+               saveAllAction = ActionFactory.SAVE_ALL.create(window);
+               register(saveAllAction);
+               // closeAllAction = ActionFactory.CLOSE_ALL.create(window);
+               // register(closeAllAction);
+
+       }
+
+       protected void fillMenuBar(IMenuManager menuBar) {
+               // MenuManager fileMenu = new MenuManager("&File",
+               // IWorkbenchActionConstants.M_FILE);
+               // MenuManager editMenu = new MenuManager("&Edit",
+               // IWorkbenchActionConstants.M_EDIT);
+               // MenuManager windowMenu = new MenuManager("&Window",
+               // IWorkbenchActionConstants.M_WINDOW);
+               //
+               // menuBar.add(fileMenu);
+               // menuBar.add(editMenu);
+               // menuBar.add(windowMenu);
+               // // Add a group marker indicating where action set menus will appear.
+               // menuBar.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
+               //
+               // // File
+               // fileMenu.add(saveAction);
+               // fileMenu.add(saveAllAction);
+               // fileMenu.add(closeAllAction);
+               // fileMenu.add(new
+               // GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
+               // fileMenu.add(new Separator());
+               // fileMenu.add(logoutAction);
+               //
+               // // Edit
+               // editMenu.add(preferences);
+               //
+               // // Window
+               // windowMenu.add(openPerspectiveDialogAction);
+               // windowMenu.add(showViewMenuAction);
+       }
+
+       @Override
+       protected void fillCoolBar(ICoolBarManager coolBar) {
+               // Add a command which label is the display name of the current
+               // logged-in user
+               if (username != null) {
+                       ICommandService cmdService = (ICommandService) getActionBarConfigurer()
+                                       .getWindowConfigurer().getWorkbenchConfigurer()
+                                       .getWorkbench().getService(ICommandService.class);
+                       Category userMenus = cmdService.getCategory(ID_BASE + ".userMenus");
+                       if (!userMenus.isDefined())
+                               userMenus.define("User Menus", "User related menus");
+                       Command userMenu = cmdService.getCommand(ID_BASE
+                                       + ".userMenuCommand");
+                       if (userMenu.isDefined())
+                               userMenu.undefine();
+                       userMenu.define(CurrentUser.getDisplayName(), "User menu actions",
+                                       userMenus);
+                       // userMenu.define(username, "User menu actions", userMenus);
+                       
+                       userMenu.setHandler(new OpenHomePerspective());
+
+                       // userToolbar.add(new UserMenuAction());
+                       // coolBar.add(userToolbar);
+               } else {// anonymous
+                       IToolBarManager userToolbar = new ToolBarManager(SWT.FLAT
+                                       | SWT.RIGHT);
+                       // userToolbar.add(logoutAction);
+                       coolBar.add(userToolbar);
+               }
+               // IToolBarManager saveToolbar = new ToolBarManager(SWT.FLAT |
+               // SWT.RIGHT);
+               // saveToolbar.add(saveAction);
+               // saveToolbar.add(saveAllAction);
+               // coolBar.add(saveToolbar);
+       }
+
+       // class UserMenuAction extends Action implements IWorkbenchAction {
+       //
+       // public UserMenuAction() {
+       // super(username, IAction.AS_DROP_DOWN_MENU);
+       // // setMenuCreator(new UserMenu());
+       // }
+       //
+       // @Override
+       // public String getId() {
+       // return "org.argeo.security.ui.rap.userMenu";
+       // }
+       //
+       // @Override
+       // public void dispose() {
+       // }
+       //
+       // }
+
+       // class UserMenu implements IMenuCreator {
+       // private Menu menu;
+       //
+       // public Menu getMenu(Control parent) {
+       // Menu menu = new Menu(parent);
+       // addActionToMenu(menu, logoutAction);
+       // return menu;
+       // }
+       //
+       // private void addActionToMenu(Menu menu, IAction action) {
+       // ActionContributionItem item = new ActionContributionItem(action);
+       // item.fill(menu, -1);
+       // }
+       //
+       // public void dispose() {
+       // if (menu != null) {
+       // menu.dispose();
+       // }
+       // }
+       //
+       // public Menu getMenu(Menu parent) {
+       // // Not use
+       // return null;
+       // }
+       //
+       // }
+
+       // protected IAction createLogoutAction() {
+       // Subject subject = Subject.getSubject(AccessController.getContext());
+       // final String username = subject.getPrincipals().iterator().next()
+       // .getName();
+       //
+       // IAction logoutAction = new Action() {
+       // public String getId() {
+       // return SecureRapActivator.ID + ".logoutAction";
+       // }
+       //
+       // public String getText() {
+       // return "Logout " + username;
+       // }
+       //
+       // public void run() {
+       // // try {
+       // // Subject subject = SecureRapActivator.getLoginContext()
+       // // .getSubject();
+       // // String subjectStr = subject.toString();
+       // // subject.getPrincipals().clear();
+       // // SecureRapActivator.getLoginContext().logout();
+       // // log.info(subjectStr + " logged out");
+       // // } catch (LoginException e) {
+       // // log.error("Error when logging out", e);
+       // // }
+       // // SecureEntryPoint.logout(username);
+       // // PlatformUI.getWorkbench().close();
+       // // try {
+       // // RWT.getRequest().getSession().setMaxInactiveInterval(1);
+       // // } catch (Exception e) {
+       // // if (log.isTraceEnabled())
+       // // log.trace("Error when invalidating session", e);
+       // // }
+       // }
+       //
+       // };
+       // return logoutAction;
+       // }
+
+}
diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWindowAdvisor.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWindowAdvisor.java
new file mode 100644 (file)
index 0000000..05f4787
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.rap;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
+import org.eclipse.ui.application.ActionBarAdvisor;
+import org.eclipse.ui.application.IActionBarConfigurer;
+import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
+import org.eclipse.ui.application.WorkbenchWindowAdvisor;
+
+/** Eclipse RAP specific window advisor */
+public class RapWindowAdvisor extends WorkbenchWindowAdvisor {
+
+       private String username;
+
+       public RapWindowAdvisor(IWorkbenchWindowConfigurer configurer,
+                       String username) {
+               super(configurer);
+               this.username = username;
+       }
+
+       @Override
+       public ActionBarAdvisor createActionBarAdvisor(
+                       IActionBarConfigurer configurer) {
+               return new RapActionBarAdvisor(configurer, username);
+       }
+
+       public void preWindowOpen() {
+               IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
+               configurer.setShowCoolBar(true);
+               configurer.setShowMenuBar(false);
+               configurer.setShowStatusLine(false);
+               configurer.setShowPerspectiveBar(true);
+               configurer.setTitle("Argeo Web UI"); //$NON-NLS-1$
+               // Full screen, see
+               // http://wiki.eclipse.org/RAP/FAQ#How_to_create_a_fullscreen_application
+               configurer.setShellStyle(SWT.NO_TRIM);
+               Rectangle bounds = Display.getCurrent().getBounds();
+               configurer.setInitialSize(new Point(bounds.width, bounds.height));
+
+               // Handle window resize in Rap 2.1+ see
+               // https://bugs.eclipse.org/bugs/show_bug.cgi?id=417254
+               Display.getCurrent().addListener(SWT.Resize, new Listener() {
+                       private static final long serialVersionUID = 2970912561866704526L;
+
+                       @Override
+                       public void handleEvent(Event event) {
+                               Rectangle bounds = event.display.getBounds();
+                               IWorkbenchWindow iww = getWindowConfigurer().getWindow()
+                                               .getWorkbench().getActiveWorkbenchWindow();
+                               iww.getShell().setBounds(bounds);
+                       }
+               });
+       }
+
+       @Override
+       public void postWindowCreate() {
+               Shell shell = getWindowConfigurer().getWindow().getShell();
+               shell.setMaximized(true);
+       }
+
+       @Override
+       public void postWindowOpen() {
+               String defaultPerspective = getWindowConfigurer()
+                               .getWorkbenchConfigurer().getWorkbench()
+                               .getPerspectiveRegistry().getDefaultPerspective();
+               if (defaultPerspective == null) {
+                       IWorkbenchWindow window = getWindowConfigurer().getWindow();
+                       if (window == null)
+                               return;
+
+                       IWorkbenchAction openPerspectiveDialogAction = ActionFactory.OPEN_PERSPECTIVE_DIALOG
+                                       .create(window);
+                       openPerspectiveDialogAction.run();
+               }
+       }
+
+}
diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java
new file mode 100644 (file)
index 0000000..edde41f
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.rap;
+
+import org.eclipse.ui.IPerspectiveDescriptor;
+import org.eclipse.ui.application.IWorkbenchConfigurer;
+import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
+import org.eclipse.ui.application.WorkbenchAdvisor;
+import org.eclipse.ui.application.WorkbenchWindowAdvisor;
+
+/** Eclipse RAP specific workbench advisor */
+public class RapWorkbenchAdvisor extends WorkbenchAdvisor {
+       public final static String INITIAL_PERSPECTIVE_PROPERTY = "org.argeo.security.ui.initialPerspective";
+       public final static String SAVE_AND_RESTORE_PROPERTY = "org.argeo.security.ui.saveAndRestore";
+
+       private String initialPerspective = System.getProperty(
+                       INITIAL_PERSPECTIVE_PROPERTY, null);
+
+       private String username;
+
+       public RapWorkbenchAdvisor(String username) {
+               this.username = username;
+       }
+
+       @Override
+       public void initialize(IWorkbenchConfigurer configurer) {
+               super.initialize(configurer);
+               Boolean saveAndRestore = Boolean.parseBoolean(System.getProperty(
+                               SAVE_AND_RESTORE_PROPERTY, "false"));
+               configurer.setSaveAndRestore(saveAndRestore);
+       }
+
+       public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(
+                       IWorkbenchWindowConfigurer configurer) {
+               return new RapWindowAdvisor(configurer, username);
+       }
+
+       public String getInitialWindowPerspectiveId() {
+               if (initialPerspective != null) {
+                       // check whether this user can see the declared perspective
+                       // (typically the perspective won't be listed if this user doesn't
+                       // have the right to see it)
+                       IPerspectiveDescriptor pd = getWorkbenchConfigurer().getWorkbench()
+                                       .getPerspectiveRegistry()
+                                       .findPerspectiveWithId(initialPerspective);
+                       if (pd == null)
+                               return null;
+               }
+               return initialPerspective;
+       }
+}
diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWorkbenchLogin.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/RapWorkbenchLogin.java
new file mode 100644 (file)
index 0000000..7bf487a
--- /dev/null
@@ -0,0 +1,93 @@
+package org.argeo.security.ui.rap;
+
+import java.security.PrivilegedAction;
+import java.util.Locale;
+
+import javax.security.auth.Subject;
+import javax.security.auth.x500.X500Principal;
+
+import org.argeo.cms.CmsMsg;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.util.LoginEntryPoint;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+
+public class RapWorkbenchLogin extends LoginEntryPoint {
+       // private final static Log log =
+       // LogFactory.getLog(RapWorkbenchLogin.class);
+
+       /** Override to provide an application specific workbench advisor */
+       protected RapWorkbenchAdvisor createRapWorkbenchAdvisor(String username) {
+               return new RapWorkbenchAdvisor(username);
+       }
+
+       @Override
+       public int createUI() {
+               JavaScriptExecutor jsExecutor = RWT.getClient().getService(
+                               JavaScriptExecutor.class);
+               int returnCode;
+               try {
+                       returnCode = super.createUI();
+               } finally {
+                       // always reload
+                       jsExecutor.execute("location.reload()");
+               }
+               return returnCode;
+       }
+
+       @Override
+       protected int postLogin() {
+               final Display display = Display.getCurrent();
+               Subject subject = getSubject();
+               if (subject.getPrincipals(X500Principal.class).isEmpty()) {
+                       RWT.getClient().getService(JavaScriptExecutor.class)
+                                       .execute("location.reload()");
+               }
+               //
+               // RUN THE WORKBENCH
+               //
+               Integer returnCode = null;
+               try {
+                       returnCode = Subject.doAs(getSubject(),
+                                       new PrivilegedAction<Integer>() {
+                                               public Integer run() {
+                                                       int result = createAndRunWorkbench(display,
+                                                                       CurrentUser.getUsername(getSubject()));
+                                                       return new Integer(result);
+                                               }
+                                       });
+                       // explicit workbench closing
+                       logout();
+               } finally {
+                       display.dispose();
+               }
+               return returnCode;
+       }
+
+       protected int createAndRunWorkbench(Display display, String username) {
+               RapWorkbenchAdvisor workbenchAdvisor = createRapWorkbenchAdvisor(username);
+               return PlatformUI.createAndRunWorkbench(display, workbenchAdvisor);
+       }
+
+       @Override
+       protected void extendsCredentialsBlock(Composite credentialsBlock,
+                       Locale selectedLocale, SelectionListener loginSelectionListener) {
+               Button loginButton = new Button(credentialsBlock, SWT.PUSH);
+               loginButton.setText(CmsMsg.login.lead(selectedLocale));
+               loginButton.setLayoutData(CmsUtils.fillWidth());
+               loginButton.addSelectionListener(loginSelectionListener);
+       }
+
+       @Override
+       protected Display createDisplay() {
+               return PlatformUI.createDisplay();
+       }
+
+}
diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java
new file mode 100644 (file)
index 0000000..a681527
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.rap;
+
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.CredentialNotFoundException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.x500.X500Principal;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.cms.auth.ThreadDeathLoginException;
+import org.argeo.cms.widgets.auth.DefaultLoginDialog;
+import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.application.EntryPoint;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * RAP entry point with login capabilities. Once the user has been
+ * authenticated, the workbench is run as a privileged action by the related
+ * subject.
+ */
+@Deprecated
+public class SecureEntryPoint implements EntryPoint {
+       final static String ACCESS_CONTROL_CONTEXT = "org.argeo.node.accessControlContext";
+       private final static Log log = LogFactory.getLog(SecureEntryPoint.class);
+
+       /**
+        * How many seconds to wait before invalidating the session if the user has
+        * not yet logged in.
+        */
+       private Integer loginTimeout = 1 * 60;
+       // TODO make it configurable
+       /** Default session timeout is 8 hours (European working day length) */
+       private Integer sessionTimeout = 8 * 60 * 60;
+
+       /** Override to provide an application specific workbench advisor */
+       protected RapWorkbenchAdvisor createRapWorkbenchAdvisor(String username) {
+               return new RapWorkbenchAdvisor(username);
+       }
+
+       @Override
+       public final int createUI() {
+               // Short login timeout so that the modal dialog login doesn't hang
+               // around too long
+               RWT.getRequest().getSession().setMaxInactiveInterval(loginTimeout);
+
+               final Display display = PlatformUI.createDisplay();
+
+               // load context from session
+               HttpServletRequest httpRequest = RWT.getRequest();
+               final HttpSession httpSession = httpRequest.getSession();
+               AccessControlContext acc = (AccessControlContext) httpSession
+                               .getAttribute(ACCESS_CONTROL_CONTEXT);
+
+               final Subject subject;
+               if (acc != null
+                               && Subject.getSubject(acc).getPrincipals(X500Principal.class)
+                                               .size() == 1) {
+                       subject = Subject.getSubject(acc);
+               } else {
+                       subject = new Subject();
+
+                       final LoginContext loginContext;
+                       DefaultLoginDialog callbackHandler;
+                       try {
+                               callbackHandler = new DefaultLoginDialog(
+                                               display.getActiveShell());
+                               loginContext = new LoginContext(
+                                               AuthConstants.LOGIN_CONTEXT_USER, subject,
+                                               callbackHandler);
+                       } catch (LoginException e1) {
+                               throw new CmsException("Cannot initialize login context", e1);
+                       }
+
+                       tryLogin: while (subject.getPrincipals(X500Principal.class).size() == 0) {
+                               try {
+                                       loginContext.login();
+                                       if (subject.getPrincipals(X500Principal.class).size() == 0)
+                                               throw new CmsException("Login succeeded but no auth");// fatal
+
+                                       // add thread locale to RWT session
+                                       // if (log.isTraceEnabled())
+                                       // log.trace("Locale " + LocaleUtils.threadLocale.get());
+                                       // RWT.setLocale(LocaleUtils.threadLocale.get());
+
+                                       // once the user is logged in, longer session timeout
+                                       RWT.getRequest().getSession()
+                                                       .setMaxInactiveInterval(sessionTimeout);
+
+                                       if (log.isDebugEnabled())
+                                               log.debug("Authenticated " + subject);
+                               } catch (FailedLoginException e) {
+                                       MessageDialog.openInformation(display.getActiveShell(),
+                                                       "Bad Credentials", e.getMessage());
+                                       // retry login
+                                       continue tryLogin;
+                               } catch (CredentialNotFoundException e) {
+                                       MessageDialog.openInformation(display.getActiveShell(),
+                                                       "No Credentials", e.getMessage());
+                                       // retry login
+                                       continue tryLogin;
+                               } catch (LoginException e) {
+                                       callbackHandler.getShell().dispose();
+                                       return processLoginDeath(display, e);
+                               }
+                       }
+               }
+               final String username = subject.getPrincipals(X500Principal.class)
+                               .iterator().next().getName();
+               // Logout callback when the display is disposed
+               display.disposeExec(new Runnable() {
+                       public void run() {
+                               if (log.isTraceEnabled())
+                                       log.trace("Display disposed");
+                               try {
+                                       LoginContext loginContext = new LoginContext(
+                                                       AuthConstants.LOGIN_CONTEXT_USER, subject);
+                                       loginContext.logout();
+                               } catch (LoginException e) {
+                                       log.error("Error when logging out", e);
+                               }
+                       }
+               });
+
+               //
+               // RUN THE WORKBENCH
+               //
+               Integer returnCode = null;
+               try {
+                       returnCode = Subject.doAs(subject, new PrivilegedAction<Integer>() {
+                               public Integer run() {
+                                       // add security context to session
+                                       httpSession.setAttribute(ACCESS_CONTROL_CONTEXT,
+                                                       AccessController.getContext());
+
+                                       // start workbench
+                                       RapWorkbenchAdvisor workbenchAdvisor = createRapWorkbenchAdvisor(username);
+                                       int result = PlatformUI.createAndRunWorkbench(display,
+                                                       workbenchAdvisor);
+                                       return new Integer(result);
+                               }
+                       });
+                       // Explicit exit from workbench
+                       fullLogout(subject, username);
+               } finally {
+                       display.dispose();
+               }
+               return returnCode;
+       }
+
+       private Integer processLoginDeath(Display display, Throwable e) {
+               // check thread death
+               ThreadDeath td = wasCausedByThreadDeath(e);
+               if (td != null) {
+                       display.dispose();
+                       throw td;
+               }
+               if (!display.isDisposed()) {
+                       ErrorFeedback.show("Unexpected exception during authentication", e);
+                       // this was not just bad credentials or death thread
+                       RWT.getRequest().getSession().setMaxInactiveInterval(1);
+                       display.dispose();
+                       return -1;
+               } else {
+                       throw new CmsException(
+                                       "Unexpected exception during authentication", e);
+               }
+
+       }
+
+       /**
+        * If there is a {@link ThreadDeath} in the root causes, rethrow it
+        * (important for RAP cleaning mechanism)
+        */
+       protected ThreadDeath wasCausedByThreadDeath(Throwable t) {
+               if (t instanceof ThreadDeath)
+                       return (ThreadDeath) t;
+               if (t instanceof ThreadDeathLoginException)
+                       return ((ThreadDeathLoginException) t).getThreadDeath();
+               if (t.getCause() != null)
+                       return wasCausedByThreadDeath(t.getCause());
+               else
+                       return null;
+       }
+
+       private void fullLogout(Subject subject, String username) {
+               try {
+                       LoginContext loginContext = new LoginContext(
+                                       AuthConstants.LOGIN_CONTEXT_USER, subject);
+                       loginContext.logout();
+                       HttpServletRequest httpRequest = RWT.getRequest();
+                       HttpSession httpSession = httpRequest.getSession();
+                       httpSession.setAttribute(ACCESS_CONTROL_CONTEXT, null);
+                       RWT.getRequest().getSession().setMaxInactiveInterval(1);
+                       log.info("Logged out " + (username != null ? username : "")
+                                       + " (THREAD=" + Thread.currentThread().getId() + ")");
+               } catch (LoginException e) {
+                       log.error("Error when logging out", e);
+               }
+       }
+}
diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/SecureRapActivator.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/SecureRapActivator.java
new file mode 100644 (file)
index 0000000..b3d7c23
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.rap;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/** Configure Equinox login context from the bundle context. */
+public class SecureRapActivator implements BundleActivator {
+       public final static String ID = "org.argeo.security.ui.rap";
+
+       private static BundleContext bundleContext;
+
+       public void start(BundleContext bc) throws Exception {
+               bundleContext = bc;
+       }
+
+       public void stop(BundleContext context) throws Exception {
+               bundleContext = null;
+       }
+
+       public static BundleContext getBundleContext() {
+               return bundleContext;
+       }
+}
diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/commands/OpenHome.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/commands/OpenHome.java
new file mode 100644 (file)
index 0000000..37ebe35
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.rap.commands;
+
+import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.argeo.eclipse.ui.workbench.CommandUtils;
+import org.argeo.security.ui.UserHomePerspective;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.WorkbenchException;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/** Default action of the user menu */
+public class OpenHome extends AbstractHandler {
+       private final static String PROP_OPEN_HOME_CMD_ID = "org.argeo.ui.openHomeCommandId";
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+
+               String defaultCmdId = System.getProperty(PROP_OPEN_HOME_CMD_ID, "");
+               if (!"".equals(defaultCmdId.trim()))
+                       CommandUtils.callCommand(defaultCmdId);
+               else {
+                       try {
+                               HandlerUtil.getActiveSite(event).getWorkbenchWindow()
+                                               .openPage(UserHomePerspective.ID, null);
+                       } catch (WorkbenchException e) {
+                               ErrorFeedback.show("Cannot open home perspective", e);
+                       }
+               }
+               return null;
+       }
+}
diff --git a/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/commands/UserMenu.java b/org.argeo.cms.ui.workbench.rap/src/org/argeo/security/ui/rap/commands/UserMenu.java
new file mode 100644 (file)
index 0000000..9867430
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.rap.commands;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+/** Default action of the user menu */
+public class UserMenu extends AbstractHandler {
+
+       @Override
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               return null;
+       }
+
+}
diff --git a/org.argeo.cms.ui.workbench/.classpath b/org.argeo.cms.ui.workbench/.classpath
new file mode 100644 (file)
index 0000000..457b115
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src" />
+       <classpathentry kind="con"
+               path="org.eclipse.pde.core.requiredPlugins" />
+       <classpathentry kind="con"
+               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" />
+       <classpathentry kind="output" path="bin" />
+</classpath>
diff --git a/org.argeo.cms.ui.workbench/.project b/org.argeo.cms.ui.workbench/.project
new file mode 100644 (file)
index 0000000..f7f7a8e
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.cms.ui.workbench</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/org.argeo.cms.ui.workbench/META-INF/spring/commands.xml b/org.argeo.cms.ui.workbench/META-INF/spring/commands.xml
new file mode 100644 (file)
index 0000000..7d39876
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+       <!-- USERS CRUDS -->
+       <bean id="newUser" class="org.argeo.security.ui.admin.internal.commands.NewUser"
+               scope="prototype">
+               <property name="userAdminWrapper" ref="userAdminWrapper" />
+       </bean>
+       <bean id="deleteUsers"
+               class="org.argeo.security.ui.admin.internal.commands.DeleteUsers"
+               scope="prototype">
+               <property name="userAdminWrapper" ref="userAdminWrapper" />
+       </bean>
+       <bean id="userBatchUpdate"
+               class="org.argeo.security.ui.admin.internal.commands.UserBatchUpdate"
+               scope="prototype">
+               <property name="userAdminWrapper" ref="userAdminWrapper" />
+       </bean>
+       <!-- GROUPS CRUDS -->
+       <bean id="newGroup" class="org.argeo.security.ui.admin.internal.commands.NewGroup"
+               scope="prototype">
+               <property name="userAdminWrapper" ref="userAdminWrapper" />
+       </bean>
+       <bean id="deleteGroups"
+               class="org.argeo.security.ui.admin.internal.commands.DeleteGroups"
+               scope="prototype">
+               <property name="userAdminWrapper" ref="userAdminWrapper" />
+       </bean>
+
+       <!-- TRANSACTIONS -->
+       <bean id="userTransactionHandler"
+               class="org.argeo.security.ui.admin.internal.commands.UserTransactionHandler"
+               scope="prototype">
+               <property name="userAdminWrapper" ref="userAdminWrapper" />
+       </bean>
+</beans>
diff --git a/org.argeo.cms.ui.workbench/META-INF/spring/common.xml b/org.argeo.cms.ui.workbench/META-INF/spring/common.xml
new file mode 100644 (file)
index 0000000..f3b7ecb
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans xmlns="http://www.springframework.org/schema/beans"\r
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
+\r
+       <bean id="userTransactionProvider"\r
+               class="org.argeo.security.ui.admin.internal.providers.UserTransactionProvider"\r
+               scope="singleton" lazy-init="false">\r
+               <property name="userTransaction" ref="userTransaction" />\r
+       </bean>\r
+\r
+       <bean id="userAdminWrapper" class="org.argeo.security.ui.admin.internal.UserAdminWrapper"\r
+               scope="singleton" lazy-init="false">\r
+               <property name="userTransaction" ref="userTransaction" />\r
+               <property name="userAdmin" ref="userAdmin" />\r
+               <property name="userAdminServiceReference" ref="userAdmin" />\r
+       </bean>\r
+\r
+       <bean\r
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">\r
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />\r
+               <property name="locations">\r
+                       <value>osgibundle:security-admin.properties</value>\r
+               </property>\r
+       </bean>\r
+\r
+</beans>
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/META-INF/spring/osgi.xml b/org.argeo.cms.ui.workbench/META-INF/spring/osgi.xml
new file mode 100644 (file)
index 0000000..02a6374
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
+       xmlns:osgi="http://www.springframework.org/schema/osgi"\r
+       xsi:schemaLocation="http://www.springframework.org/schema/osgi  \r
+       http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
+       http://www.springframework.org/schema/beans   \r
+       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"\r
+       osgi:default-timeout="30000">\r
+\r
+       <reference id="nodeRepository" interface="javax.jcr.Repository"\r
+               filter="(argeo.jcr.repository.alias=node)" />\r
+               \r
+       <!-- New user admin -->\r
+       <reference id="userAdmin" interface="org.osgi.service.useradmin.UserAdmin" />\r
+       <reference id="userTransaction" interface="javax.transaction.UserTransaction" />\r
+</beans:beans>
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/META-INF/spring/parts.xml b/org.argeo.cms.ui.workbench/META-INF/spring/parts.xml
new file mode 100644 (file)
index 0000000..cbc36e0
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+
+       <!-- Editors -->
+       <bean id="userEditor" class="org.argeo.security.ui.admin.internal.parts.UserEditor"
+               scope="prototype">
+               <property name="userAdminWrapper" ref="userAdminWrapper" />
+       </bean>
+
+       <bean id="groupEditor" class="org.argeo.security.ui.admin.internal.parts.UserEditor"
+               scope="prototype">
+               <property name="userAdminWrapper" ref="userAdminWrapper" />
+       </bean>
+
+       <!-- Views -->
+       <bean id="usersView" class="org.argeo.security.ui.admin.internal.parts.UsersView"
+               scope="prototype">
+               <property name="userAdminWrapper" ref="userAdminWrapper" />
+       </bean>
+       <bean id="groupsView" class="org.argeo.security.ui.admin.internal.parts.GroupsView"
+               scope="prototype">
+               <property name="userAdminWrapper" ref="userAdminWrapper" />
+       </bean>
+
+</beans>
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/bnd.bnd b/org.argeo.cms.ui.workbench/bnd.bnd
new file mode 100644 (file)
index 0000000..4ace8db
--- /dev/null
@@ -0,0 +1,16 @@
+Bundle-SymbolicName: org.argeo.cms.ui.workbench;singleton:=true
+Bundle-Activator: org.argeo.security.ui.admin.SecurityAdminPlugin
+Bundle-ActivationPolicy: lazy
+
+Require-Bundle:        org.eclipse.core.runtime
+
+Import-Package:        org.eclipse.core.runtime.jobs,\
+org.argeo.cms.auth,\
+org.argeo.eclipse.spring,\
+org.eclipse.jface.window,\
+org.eclipse.swt,\
+org.eclipse.swt.widgets,\
+org.eclipse.ui.services,\
+org.osgi.framework,\
+org.springframework.core,\
+*                              
diff --git a/org.argeo.cms.ui.workbench/build.properties b/org.argeo.cms.ui.workbench/build.properties
new file mode 100644 (file)
index 0000000..30f7153
--- /dev/null
@@ -0,0 +1 @@
+source.. = src/
diff --git a/org.argeo.cms.ui.workbench/icons/add.gif b/org.argeo.cms.ui.workbench/icons/add.gif
new file mode 100644 (file)
index 0000000..252d7eb
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/add.gif differ
diff --git a/org.argeo.cms.ui.workbench/icons/batch.gif b/org.argeo.cms.ui.workbench/icons/batch.gif
new file mode 100644 (file)
index 0000000..b8ca14a
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/batch.gif differ
diff --git a/org.argeo.cms.ui.workbench/icons/begin.gif b/org.argeo.cms.ui.workbench/icons/begin.gif
new file mode 100755 (executable)
index 0000000..feb8e94
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/begin.gif differ
diff --git a/org.argeo.cms.ui.workbench/icons/clear.gif b/org.argeo.cms.ui.workbench/icons/clear.gif
new file mode 100644 (file)
index 0000000..6bc10f9
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/clear.gif differ
diff --git a/org.argeo.cms.ui.workbench/icons/commit.gif b/org.argeo.cms.ui.workbench/icons/commit.gif
new file mode 100755 (executable)
index 0000000..876f3eb
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/commit.gif differ
diff --git a/org.argeo.cms.ui.workbench/icons/refresh.png b/org.argeo.cms.ui.workbench/icons/refresh.png
new file mode 100644 (file)
index 0000000..a3884fb
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/refresh.png differ
diff --git a/org.argeo.cms.ui.workbench/icons/remove.gif b/org.argeo.cms.ui.workbench/icons/remove.gif
new file mode 100644 (file)
index 0000000..0ae6dec
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/remove.gif differ
diff --git a/org.argeo.cms.ui.workbench/icons/role.gif b/org.argeo.cms.ui.workbench/icons/role.gif
new file mode 100644 (file)
index 0000000..274a850
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/role.gif differ
diff --git a/org.argeo.cms.ui.workbench/icons/rollback.gif b/org.argeo.cms.ui.workbench/icons/rollback.gif
new file mode 100755 (executable)
index 0000000..c753995
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/rollback.gif differ
diff --git a/org.argeo.cms.ui.workbench/icons/save.gif b/org.argeo.cms.ui.workbench/icons/save.gif
new file mode 100644 (file)
index 0000000..654ad7b
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/save.gif differ
diff --git a/org.argeo.cms.ui.workbench/icons/save_security.png b/org.argeo.cms.ui.workbench/icons/save_security.png
new file mode 100644 (file)
index 0000000..ca41dc9
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/save_security.png differ
diff --git a/org.argeo.cms.ui.workbench/icons/save_security_disabled.png b/org.argeo.cms.ui.workbench/icons/save_security_disabled.png
new file mode 100644 (file)
index 0000000..fb7d08d
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/save_security_disabled.png differ
diff --git a/org.argeo.cms.ui.workbench/icons/security.gif b/org.argeo.cms.ui.workbench/icons/security.gif
new file mode 100644 (file)
index 0000000..57fb95e
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/security.gif differ
diff --git a/org.argeo.cms.ui.workbench/icons/sync.gif b/org.argeo.cms.ui.workbench/icons/sync.gif
new file mode 100644 (file)
index 0000000..b4fa052
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/sync.gif differ
diff --git a/org.argeo.cms.ui.workbench/icons/user.gif b/org.argeo.cms.ui.workbench/icons/user.gif
new file mode 100644 (file)
index 0000000..90a0014
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/user.gif differ
diff --git a/org.argeo.cms.ui.workbench/icons/users.gif b/org.argeo.cms.ui.workbench/icons/users.gif
new file mode 100644 (file)
index 0000000..2de7edd
Binary files /dev/null and b/org.argeo.cms.ui.workbench/icons/users.gif differ
diff --git a/org.argeo.cms.ui.workbench/plugin.xml b/org.argeo.cms.ui.workbench/plugin.xml
new file mode 100644 (file)
index 0000000..2cf0ba2
--- /dev/null
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+   <extension
+         point="org.eclipse.ui.perspectives">
+      <perspective
+            class="org.argeo.security.ui.admin.SecurityAdminPerspective"
+            icon="icons/security.gif"
+            id="org.argeo.security.ui.admin.adminSecurityPerspective"
+            name="Security">
+      </perspective>
+   </extension>
+   
+   <!-- VIEWS -->
+   <extension
+               point="org.eclipse.ui.views">
+      <view
+            class="org.argeo.eclipse.spring.SpringExtensionFactory"
+            icon="icons/users.gif"
+            id="org.argeo.security.ui.admin.usersView"
+            name="Users"
+            restorable="true">
+      </view>
+      <view
+            class="org.argeo.eclipse.spring.SpringExtensionFactory"
+            icon="icons/role.gif"
+            id="org.argeo.security.ui.admin.groupsView"
+            name="Groups"
+            restorable="false">
+      </view>
+    </extension> 
+       
+       <!-- EDITORS -->
+       <extension
+               point="org.eclipse.ui.editors">
+               <editor
+                       class="org.argeo.eclipse.spring.SpringExtensionFactory"
+            id="org.argeo.security.ui.admin.userEditor"
+            name="User"
+            icon="icons/user.gif"
+            default="false">
+               </editor>
+               <editor
+                       class="org.argeo.eclipse.spring.SpringExtensionFactory"
+            id="org.argeo.security.ui.admin.groupEditor"
+            name="User"
+            icon="icons/users.gif"
+            default="false">
+               </editor>
+       </extension>
+    
+    <extension
+         point="org.eclipse.ui.commands">
+               <!-- User CRUD -->
+               <command
+            id="org.argeo.security.ui.admin.newUser"
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            name="New User">
+       </command>
+               <command
+                       id="org.argeo.security.ui.admin.deleteUsers"
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+                       name="Delete User">
+               </command>
+               <command
+                       defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            id="org.argeo.security.ui.admin.userBatchUpdate"
+            name="User batch update">
+               </command>
+               <!-- Group CRUD -->
+               <command
+                       id="org.argeo.security.ui.admin.newGroup"
+                       defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            name="New Group">
+               </command>
+               <command
+            id="org.argeo.security.ui.admin.deleteGroups"
+                       defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            name="Delete Group">
+               </command>
+               <!-- Transaction -->
+               <command
+                   id="org.argeo.security.ui.admin.userTransactionHandler"
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+               name="Manage a user transaction">
+                       <commandParameter
+                                       id="param.commandId"
+                                       name="begin, commit or rollback">
+                       </commandParameter>
+               </command>
+
+         <!-- Force the refresh when the various listener are not enough -->
+      <command
+            defaultHandler="org.argeo.security.ui.admin.internal.commands.ForceRefresh"
+            id="org.argeo.security.ui.admin.forceRefresh"
+            name="Force Refresh">
+      </command>
+       </extension>
+       
+       <!-- MENU CONTRIBUTIONS -->
+       <extension
+               point="org.eclipse.ui.menus">
+               <menuContribution
+                       locationURI="toolbar:org.argeo.security.ui.rap.userToolbar?after=org.eclipse.ui.file.saveAll"> 
+                       <!-- Transaction management --> 
+                       <command
+                               commandId="org.argeo.security.ui.admin.userTransactionHandler"
+                               icon="icons/commit.gif"
+                               label="Commit Transaction"
+                               style="push"
+                               tooltip="Commit a user transaction">
+                               <parameter name="param.commandId" value="transaction.commit" />
+                               <visibleWhen>
+                                       <with variable="org.argeo.security.ui.admin.userTransactionState">
+                                               <equals value="status.active" />
+                                       </with>
+                               </visibleWhen>
+                       </command>
+                       <command
+                               commandId="org.argeo.security.ui.admin.userTransactionHandler"
+                               icon="icons/rollback.gif"
+                               label="Rollback Transaction"
+                               style="push"
+                               tooltip="Abandon current changes and rollback to the latest commited version">
+                               <parameter name="param.commandId" value="transaction.rollback" />
+                               <visibleWhen>
+                                       <with variable="org.argeo.security.ui.admin.userTransactionState">
+                                                       <equals value="status.active" />
+                                       </with>
+                               </visibleWhen>
+                       </command>
+               </menuContribution>
+    
+       <!-- UsersView specific toolbar menu -->
+               <menuContribution
+            locationURI="toolbar:org.argeo.security.ui.admin.usersView">
+            <command
+                  commandId="org.argeo.security.ui.admin.deleteUsers"
+                  icon="icons/remove.gif"
+                  label="Delete User"
+                  tooltip="Delete selected users">
+            </command>
+            <command
+                  commandId="org.argeo.security.ui.admin.forceRefresh"
+                  icon="icons/refresh.png"
+                  label="Refresh list"
+                  tooltip="Force the full refresh of the user list">
+            </command>
+            <command
+                  commandId="org.argeo.security.ui.admin.newUser"
+                  icon="icons/add.gif"
+                  label="Add User"
+                  tooltip="Create a new user">
+            </command>
+            <command
+                  commandId="org.argeo.security.ui.admin.userBatchUpdate"
+                  icon="icons/batch.gif"
+                  label="Update users"
+                  tooltip="Perform maintenance activities on a list of chosen users">
+            </command>
+        </menuContribution>
+
+       <!-- GroupsView specific toolbar menu -->
+        <menuContribution
+            locationURI="toolbar:org.argeo.security.ui.admin.groupsView">
+            <command
+                  commandId="org.argeo.security.ui.admin.deleteGroups"
+                  icon="icons/remove.gif"
+                  label="Delete Group"
+                  tooltip="Delete selected groups">
+            </command>
+            <command
+                  commandId="org.argeo.security.ui.admin.forceRefresh"
+                  icon="icons/refresh.png"
+                  label="Refresh list"
+                  tooltip="Force the full refresh of the group list">
+            </command>
+            <command
+                  commandId="org.argeo.security.ui.admin.newGroup"
+                  icon="icons/add.gif"
+                  label="Add Group"
+                  tooltip="Create a new group">
+            </command>
+        </menuContribution>
+         
+               <!--            <menuContribution
+            locationURI="toolbar:org.argeo.security.ui.admin.adminRolesView">
+            <command
+                  commandId="org.argeo.security.ui.admin.refreshRoles"
+                  icon="icons/sync.gif"
+                  label="LDAP Roles Sync"
+                  tooltip="Synchronize roles from LDAP">
+            </command>
+        </menuContribution> -->
+       </extension>
+
+       <!-- SERVICES -->
+       <extension
+       point="org.eclipse.ui.services">
+        <sourceProvider
+               id="org.argeo.security.ui.admin.userTransactionProvider"
+            provider="org.argeo.eclipse.spring.SpringExtensionFactory" >
+                       <variable
+                   name="org.argeo.security.ui.admin.userTransactionState"
+                   priorityLevel="workbench">
+               </variable>
+               </sourceProvider>
+       </extension>
+  
+       <!-- ACTIVITIES -->
+       <extension
+               point="org.eclipse.ui.activities">
+               <!-- group admin is intended to make all user and group maintenance operations -->
+               <!--<activityPatternBinding
+                       activityId="org.argeo.security.ui.userAdminActivity"
+                       isEqualityPattern="true"
+                       pattern="org.argeo.security.ui.admin/org.argeo.security.ui.admin.adminSecurityPerspective">
+               </activityPatternBinding>-->
+               <activityPatternBinding
+                       activityId="org.argeo.security.ui.groupAdminActivity"
+                       isEqualityPattern="true"
+                       pattern="org.argeo.security.ui.admin/org.argeo.security.ui.admin.adminSecurityPerspective">
+               </activityPatternBinding>
+       </extension>
+       
+       <!-- STARTUP  --> 
+       <extension point="org.eclipse.ui.startup">
+               <startup class="org.argeo.security.ui.admin.internal.PartStateChanged"/>
+       </extension>
+</plugin>
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/pom.xml b/org.argeo.cms.ui.workbench/pom.xml
new file mode 100644 (file)
index 0000000..ead8e2e
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <version>2.1.46-SNAPSHOT</version>
+               <artifactId>argeo-commons</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.cms.ui.workbench</artifactId>
+       <name>CMS Workbench</name>
+       <packaging>jar</packaging>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.cms</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.security.ui</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.enterprise</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.eclipse.ui</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.eclipse.ui.rap</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+                       <scope>provided</scope>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/security-admin.properties b/org.argeo.cms.ui.workbench/security-admin.properties
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminImages.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminImages.java
new file mode 100644 (file)
index 0000000..f15f8ec
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Argeo Connect - Data management and communications
+ * Copyright (C) 2012 Argeo GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * Additional permission under GNU GPL version 3 section 7
+ *
+ * If you modify this Program, or any covered work, by linking or combining it
+ * with software covered by the terms of the Eclipse Public License, the
+ * licensors of this Program grant you additional permission to convey the
+ * resulting work. Corresponding Source for a non-source form of such a
+ * combination shall include the source code for the parts of such software
+ * which are used as well as that of the covered work.
+ */
+package org.argeo.security.ui.admin;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+
+/** Shared icons that must be declared programmatically . */
+public class SecurityAdminImages {
+       private final static String PREFIX = "icons/";
+
+       public final static ImageDescriptor ICON_REMOVE_DESC = SecurityAdminPlugin
+                       .getImageDescriptor(PREFIX + "remove.gif");
+       public final static ImageDescriptor ICON_USER_DESC = SecurityAdminPlugin
+                       .getImageDescriptor(PREFIX + "user.gif");
+       
+       public final static Image ICON_USER = ICON_USER_DESC.createImage();
+       public final static Image ICON_GROUP = SecurityAdminPlugin
+                       .getImageDescriptor(PREFIX + "users.gif").createImage();
+       public final static Image ICON_ROLE = SecurityAdminPlugin
+                       .getImageDescriptor(PREFIX + "role.gif").createImage();
+
+}
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminPerspective.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminPerspective.java
new file mode 100644 (file)
index 0000000..2334827
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin;
+
+import org.argeo.security.ui.admin.internal.parts.GroupsView;
+import org.argeo.security.ui.admin.internal.parts.UsersView;
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+
+/** Default perspective to manage users and groups */
+public class SecurityAdminPerspective implements IPerspectiveFactory {
+       public void createInitialLayout(IPageLayout layout) {
+               String editorArea = layout.getEditorArea();
+               layout.setEditorAreaVisible(true);
+               layout.setFixed(false);
+
+               IFolderLayout left = layout.createFolder("left", IPageLayout.LEFT,
+                               0.25f, editorArea);
+               left.addView(UsersView.ID);
+               left.addView(GroupsView.ID);
+       }
+}
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminPlugin.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/SecurityAdminPlugin.java
new file mode 100644 (file)
index 0000000..f9c0ad9
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+public class SecurityAdminPlugin extends AbstractUIPlugin {
+       public static final String PLUGIN_ID = "org.argeo.security.ui.admin"; //$NON-NLS-1$
+       private static SecurityAdminPlugin plugin;
+       private static BundleContext bundleContext;
+
+       public SecurityAdminPlugin() {
+       }
+
+       public void start(BundleContext context) throws Exception {
+               super.start(context);
+               plugin = this;
+               bundleContext = context;
+       }
+
+       public void stop(BundleContext context) throws Exception {
+               plugin = null;
+               bundleContext = null;
+               super.stop(context);
+       }
+
+       public static SecurityAdminPlugin getDefault() {
+               return plugin;
+       }
+
+       public static BundleContext getBundleContext() {
+               return bundleContext;
+       }
+
+       public static ImageDescriptor getImageDescriptor(String path) {
+               return imageDescriptorFromPlugin(PLUGIN_ID, path);
+       }
+
+}
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/PartStateChanged.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/PartStateChanged.java
new file mode 100644 (file)
index 0000000..aacf6ef
--- /dev/null
@@ -0,0 +1,58 @@
+package org.argeo.security.ui.admin.internal;
+
+import org.argeo.cms.CmsException;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IStartup;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.PlatformUI;
+
+/** Manage transaction and part refresh while updating the security model */
+public class PartStateChanged implements IPartListener, IStartup {
+       // private final static Log log = LogFactory.getLog(PartStateChanged.class);
+       // private IContextActivation contextActivation;
+
+       @Override
+       public void earlyStartup() {
+               Display.getDefault().asyncExec(new Runnable() {
+                       public void run() {
+                               try {
+                                       IWorkbenchPage iwp = PlatformUI.getWorkbench()
+                                                       .getActiveWorkbenchWindow().getActivePage();
+                                       if (iwp != null)
+                                               iwp.addPartListener(new PartStateChanged());
+                               } catch (Exception e) {
+                                       throw new CmsException(
+                                                       "Error while registering the PartStateChangedListener",
+                                                       e);
+                               }
+                       }
+               });
+       }
+
+       @Override
+       public void partActivated(IWorkbenchPart part) {
+               // Nothing to do
+       }
+
+       @Override
+       public void partBroughtToTop(IWorkbenchPart part) {
+               // Nothing to do
+       }
+
+       @Override
+       public void partClosed(IWorkbenchPart part) {
+               // Nothing to do
+       }
+
+       @Override
+       public void partDeactivated(IWorkbenchPart part) {
+               // Nothing to do
+       }
+
+       @Override
+       public void partOpened(IWorkbenchPart part) {
+               // Nothing to do
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UiAdminUtils.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UiAdminUtils.java
new file mode 100644 (file)
index 0000000..871877a
--- /dev/null
@@ -0,0 +1,32 @@
+package org.argeo.security.ui.admin.internal;
+
+import javax.transaction.UserTransaction;
+
+import org.argeo.cms.CmsException;
+import org.argeo.security.ui.admin.internal.providers.UserTransactionProvider;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.services.ISourceProviderService;
+
+/** First effort to centralize back end methods used by the user admin UI */
+public class UiAdminUtils {
+       /*
+        * INTERNAL METHODS: Below methods are meant to stay here and are not part
+        * of a potential generic backend to manage the useradmin
+        */
+       /** Easily notify the ActiveWindow that the transaction had a state change */
+       public final static void notifyTransactionStateChange(
+                       UserTransaction userTransaction) {
+               try {
+                       IWorkbenchWindow aww = PlatformUI.getWorkbench()
+                                       .getActiveWorkbenchWindow();
+                       ISourceProviderService sourceProviderService = (ISourceProviderService) aww
+                                       .getService(ISourceProviderService.class);
+                       UserTransactionProvider esp = (UserTransactionProvider) sourceProviderService
+                                       .getSourceProvider(UserTransactionProvider.TRANSACTION_STATE);
+                       esp.fireTransactionStateChange();
+               } catch (Exception e) {
+                       throw new CmsException("Unable to begin transaction", e);
+               }
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UiUserAdminListener.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UiUserAdminListener.java
new file mode 100644 (file)
index 0000000..98c9836
--- /dev/null
@@ -0,0 +1,27 @@
+package org.argeo.security.ui.admin.internal;
+
+import org.eclipse.swt.widgets.Display;
+import org.osgi.service.useradmin.UserAdminEvent;
+import org.osgi.service.useradmin.UserAdminListener;
+
+/** Convenience class to insure the call to refresh is done in the UI thread */
+public abstract class UiUserAdminListener implements UserAdminListener {
+
+       private final Display display;
+
+       public UiUserAdminListener(Display display) {
+               this.display = display;
+       }
+
+       @Override
+       public void roleChanged(final UserAdminEvent event) {
+               display.asyncExec(new Runnable() {
+                       @Override
+                       public void run() {
+                               roleChangedToUiThread(event);
+                       }
+               });
+       }
+
+       public abstract void roleChangedToUiThread(UserAdminEvent event);
+}
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UserAdminWrapper.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/UserAdminWrapper.java
new file mode 100644 (file)
index 0000000..d443a3d
--- /dev/null
@@ -0,0 +1,77 @@
+package org.argeo.security.ui.admin.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.transaction.Status;
+import javax.transaction.UserTransaction;
+
+import org.argeo.cms.CmsException;
+import org.osgi.service.useradmin.UserAdminEvent;
+import org.osgi.service.useradmin.UserAdminListener;
+
+/** Centralise interaction with the UserAdmin in this bundle */
+public class UserAdminWrapper extends
+               org.argeo.cms.util.useradmin.UserAdminWrapper {
+
+       // First effort to simplify UX while managing users and groups
+       public final static boolean COMMIT_ON_SAVE = true;
+
+       // Registered listeners
+       List<UserAdminListener> listeners = new ArrayList<UserAdminListener>();
+
+       /**
+        * Starts a transaction if necessary. Should always been called together
+        * with {@link UserAdminWrapper#commitOrNotifyTransactionStateChange()} once
+        * the security model changes have been performed.
+        */
+       public UserTransaction beginTransactionIfNeeded() {
+               try {
+                       UserTransaction userTransaction = getUserTransaction();
+                       if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) {
+                               userTransaction.begin();
+                               // UiAdminUtils.notifyTransactionStateChange(userTransaction);
+                       }
+                       return userTransaction;
+               } catch (Exception e) {
+                       throw new CmsException("Unable to begin transaction", e);
+               }
+       }
+
+       /**
+        * Depending on the current application configuration, it will either commit
+        * the current transaction or throw a notification that the transaction
+        * state has changed (In the later case, it must be called from the UI
+        * thread).
+        */
+       public void commitOrNotifyTransactionStateChange() {
+               try {
+                       UserTransaction userTransaction = getUserTransaction();
+                       if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION)
+                               return;
+
+                       if (UserAdminWrapper.COMMIT_ON_SAVE)
+                               userTransaction.commit();
+                       else
+                               UiAdminUtils.notifyTransactionStateChange(userTransaction);
+               } catch (Exception e) {
+                       throw new CmsException("Unable to clean transaction", e);
+               }
+       }
+
+       // TODO implement safer mechanism
+       public void addListener(UserAdminListener userAdminListener) {
+               if (!listeners.contains(userAdminListener))
+                       listeners.add(userAdminListener);
+       }
+
+       public void removeListener(UserAdminListener userAdminListener) {
+               if (listeners.contains(userAdminListener))
+                       listeners.remove(userAdminListener);
+       }
+
+       public void notifyListeners(UserAdminEvent event) {
+               for (UserAdminListener listener : listeners)
+                       listener.roleChanged(event);
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/DeleteGroups.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/DeleteGroups.java
new file mode 100644 (file)
index 0000000..df5d430
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.commands;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.argeo.security.ui.admin.internal.UserAdminWrapper;
+import org.argeo.security.ui.admin.internal.parts.UserEditorInput;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Delete the selected groups */
+public class DeleteGroups extends AbstractHandler {
+       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
+                       + ".deleteGroups";
+
+       /* DEPENDENCY INJECTION */
+       private UserAdminWrapper userAdminWrapper;
+
+       @SuppressWarnings("unchecked")
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               ISelection selection = HandlerUtil.getCurrentSelection(event);
+               if (selection.isEmpty())
+                       return null;
+
+               List<Group> groups = new ArrayList<Group>();
+               Iterator<Group> it = ((IStructuredSelection) selection).iterator();
+               StringBuilder builder = new StringBuilder();
+               while (it.hasNext()) {
+                       Group currGroup = it.next();
+                       String groupName = UserAdminUtils.getUsername(currGroup);
+                       // TODO add checks
+                       builder.append(groupName).append("; ");
+                       groups.add(currGroup);
+               }
+
+               if (!MessageDialog.openQuestion(HandlerUtil.getActiveShell(event),
+                               "Delete Groups",
+                               "Are you sure that you " + "want to delete these groups?\n"
+                                               + builder.substring(0, builder.length() - 2)))
+                       return null;
+
+               userAdminWrapper.beginTransactionIfNeeded();
+               UserAdmin userAdmin = userAdminWrapper.getUserAdmin();
+               IWorkbenchPage iwp = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage();
+               for (Group group : groups) {
+                       String groupName = group.getName();
+                       // TODO find a way to close the editor cleanly if opened. Cannot be
+                       // done through the UserAdminListeners, it causes a
+                       // java.util.ConcurrentModificationException because disposing the
+                       // editor unregisters and disposes the listener
+                       IEditorPart part = iwp.findEditor(new UserEditorInput(groupName));
+                       if (part != null)
+                               iwp.closeEditor(part, false);
+                       userAdmin.removeRole(groupName);
+               }
+               userAdminWrapper.commitOrNotifyTransactionStateChange();
+
+               // Update the view
+               for (Group group : groups) {
+                       userAdminWrapper.notifyListeners(new UserAdminEvent(null,
+                                       UserAdminEvent.ROLE_REMOVED, group));
+               }
+
+               return null;
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/DeleteUsers.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/DeleteUsers.java
new file mode 100644 (file)
index 0000000..e583bef
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.commands;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.argeo.security.ui.admin.internal.UserAdminWrapper;
+import org.argeo.security.ui.admin.internal.parts.UserEditorInput;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Delete the selected users */
+public class DeleteUsers extends AbstractHandler {
+       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
+                       + ".deleteUsers";
+
+       /* DEPENDENCY INJECTION */
+       private UserAdminWrapper userAdminWrapper;
+
+       @SuppressWarnings("unchecked")
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               ISelection selection = HandlerUtil.getCurrentSelection(event);
+               if (selection.isEmpty())
+                       return null;
+
+               Iterator<User> it = ((IStructuredSelection) selection).iterator();
+               List<User> users = new ArrayList<User>();
+               StringBuilder builder = new StringBuilder();
+
+               while (it.hasNext()) {
+                       User currUser = it.next();
+                       String userName = UserAdminUtils.getUsername(currUser);
+                       if (UserAdminUtils.isCurrentUser(currUser)) {
+                               MessageDialog.openError(HandlerUtil.getActiveShell(event),
+                                               "Deletion forbidden",
+                                               "You cannot delete your own user this way.");
+                               return null;
+                       }
+                       builder.append(userName).append("; ");
+                       users.add(currUser);
+               }
+
+               if (!MessageDialog.openQuestion(
+                               HandlerUtil.getActiveShell(event),
+                               "Delete Users",
+                               "Are you sure that you want to delete these users?\n"
+                                               + builder.substring(0, builder.length() - 2)))
+                       return null;
+
+               userAdminWrapper.beginTransactionIfNeeded();
+               UserAdmin userAdmin = userAdminWrapper.getUserAdmin();
+               IWorkbenchPage iwp = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage();
+
+               for (User user : users) {
+                       String userName = user.getName();
+                       // TODO find a way to close the editor cleanly if opened. Cannot be
+                       // done through the UserAdminListeners, it causes a
+                       // java.util.ConcurrentModificationException because disposing the
+                       // editor unregisters and disposes the listener
+                       IEditorPart part = iwp.findEditor(new UserEditorInput(userName));
+                       if (part != null)
+                               iwp.closeEditor(part, false);
+                       userAdmin.removeRole(userName);
+               }
+               userAdminWrapper.commitOrNotifyTransactionStateChange();
+
+               for (User user : users) {
+                       userAdminWrapper.notifyListeners(new UserAdminEvent(null,
+                                       UserAdminEvent.ROLE_REMOVED, user));
+               }
+               return null;
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/ForceRefresh.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/ForceRefresh.java
new file mode 100644 (file)
index 0000000..231c871
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.commands;
+
+import org.argeo.security.ui.admin.internal.parts.GroupsView;
+import org.argeo.security.ui.admin.internal.parts.UsersView;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/** Retrieve the active view or editor and call forceRefresh method if defined */
+public class ForceRefresh extends AbstractHandler {
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               IWorkbenchWindow iww = HandlerUtil.getActiveWorkbenchWindow(event);
+               if (iww == null)
+                       return null;
+               IWorkbenchPage activePage = iww.getActivePage();
+               IWorkbenchPart part = activePage.getActivePart();
+               if (part instanceof UsersView)
+                       ((UsersView) part).refresh();
+               else if (part instanceof GroupsView)
+                       ((GroupsView) part).refresh();
+               return null;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/NewGroup.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/NewGroup.java
new file mode 100644 (file)
index 0000000..75b9b0b
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.commands;
+
+import java.util.Dictionary;
+import java.util.Map;
+
+import org.argeo.cms.CmsException;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.osgi.useradmin.LdifName;
+import org.argeo.osgi.useradmin.UserAdminConf;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.argeo.security.ui.admin.internal.UserAdminWrapper;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Create a new group */
+public class NewGroup extends AbstractHandler {
+       public final static String ID = SecurityAdminPlugin.PLUGIN_ID + ".newGroup";
+
+       /* DEPENDENCY INJECTION */
+       private UserAdminWrapper userAdminWrapper;
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               NewGroupWizard newGroupWizard = new NewGroupWizard();
+               newGroupWizard.setWindowTitle("Group creation");
+               WizardDialog dialog = new WizardDialog(
+                               HandlerUtil.getActiveShell(event), newGroupWizard);
+               dialog.open();
+               return null;
+       }
+
+       private class NewGroupWizard extends Wizard {
+
+               // Pages
+               private MainGroupInfoWizardPage mainGroupInfo;
+
+               // UI fields
+               private Text dNameTxt, commonNameTxt, descriptionTxt;
+               private Combo baseDnCmb;
+
+               public NewGroupWizard() {
+               }
+
+               @Override
+               public void addPages() {
+                       mainGroupInfo = new MainGroupInfoWizardPage();
+                       addPage(mainGroupInfo);
+               }
+
+               @SuppressWarnings({ "rawtypes", "unchecked" })
+               @Override
+               public boolean performFinish() {
+                       if (!canFinish())
+                               return false;
+                       String commonName = commonNameTxt.getText();
+                       try {
+                               userAdminWrapper.beginTransactionIfNeeded();
+                               String dn = getDn(commonName);
+                               Group group = (Group) userAdminWrapper.getUserAdmin()
+                                               .createRole(dn, Role.GROUP);
+                               Dictionary props = group.getProperties();
+                               String descStr = descriptionTxt.getText();
+                               if (EclipseUiUtils.notEmpty(descStr))
+                                       props.put(LdifName.description.name(), descStr);
+                               userAdminWrapper.commitOrNotifyTransactionStateChange();
+                               userAdminWrapper.notifyListeners(new UserAdminEvent(null,
+                                               UserAdminEvent.ROLE_CREATED, group));
+                               return true;
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Cannot create new group " + commonName, e);
+                               return false;
+                       }
+               }
+
+               private class MainGroupInfoWizardPage extends WizardPage implements
+                               FocusListener, ArgeoNames {
+                       private static final long serialVersionUID = -3150193365151601807L;
+
+                       public MainGroupInfoWizardPage() {
+                               super("Main");
+                               setTitle("General information");
+                               setMessage("Please choose a domain, provide a common name "
+                                               + "and a free description");
+                       }
+
+                       @Override
+                       public void createControl(Composite parent) {
+                               Composite bodyCmp = new Composite(parent, SWT.NONE);
+                               setControl(bodyCmp);
+                               bodyCmp.setLayout(new GridLayout(2, false));
+
+                               dNameTxt = EclipseUiUtils.createGridLT(bodyCmp,
+                                               "Distinguished name");
+                               dNameTxt.setEnabled(false);
+
+                               baseDnCmb = createGridLC(bodyCmp, "Base DN");
+                               // Initialise before adding the listener to avoid NPE
+                               initialiseDnCmb(baseDnCmb);
+                               baseDnCmb.addFocusListener(this);
+
+                               commonNameTxt = EclipseUiUtils.createGridLT(bodyCmp,
+                                               "Common name");
+                               commonNameTxt.addFocusListener(this);
+
+                               Label descLbl = new Label(bodyCmp, SWT.LEAD);
+                               descLbl.setText("Description");
+                               descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false,
+                                               false));
+                               descriptionTxt = new Text(bodyCmp, SWT.LEAD | SWT.MULTI
+                                               | SWT.WRAP | SWT.BORDER);
+                               descriptionTxt.setLayoutData(EclipseUiUtils.fillAll());
+                               descriptionTxt.addFocusListener(this);
+
+                               // Initialize buttons
+                               setPageComplete(false);
+                               getContainer().updateButtons();
+                       }
+
+                       @Override
+                       public void focusLost(FocusEvent event) {
+                               String name = commonNameTxt.getText();
+                               if (EclipseUiUtils.isEmpty(name))
+                                       dNameTxt.setText("");
+                               else
+                                       dNameTxt.setText(getDn(name));
+
+                               String message = checkComplete();
+                               if (message != null) {
+                                       setMessage(message, WizardPage.ERROR);
+                                       setPageComplete(false);
+                               } else {
+                                       setMessage("Complete", WizardPage.INFORMATION);
+                                       setPageComplete(true);
+                               }
+                               getContainer().updateButtons();
+                       }
+
+                       @Override
+                       public void focusGained(FocusEvent event) {
+                       }
+
+                       /** @return the error message or null if complete */
+                       protected String checkComplete() {
+                               String name = commonNameTxt.getText();
+
+                               if (name.trim().equals(""))
+                                       return "Common name must not be empty";
+                               Role role = userAdminWrapper.getUserAdmin()
+                                               .getRole(getDn(name));
+                               if (role != null)
+                                       return "Group " + name + " already exists";
+                               return null;
+                       }
+
+                       @Override
+                       public void setVisible(boolean visible) {
+                               super.setVisible(visible);
+                               if (visible)
+                                       if (baseDnCmb.getSelectionIndex() == -1)
+                                               baseDnCmb.setFocus();
+                                       else
+                                               commonNameTxt.setFocus();
+                       }
+               }
+
+               private Map<String, String> getDns() {
+                       return userAdminWrapper.getKnownBaseDns(true);
+               }
+
+               private String getDn(String cn) {
+                       Map<String, String> dns = getDns();
+                       String bdn = baseDnCmb.getText();
+                       if (EclipseUiUtils.notEmpty(bdn)) {
+                               Dictionary<String, ?> props = UserAdminConf.uriAsProperties(dns
+                                               .get(bdn));
+                               String dn = LdifName.cn.name() + "=" + cn + ","
+                                               + UserAdminConf.groupBase.getValue(props) + "," + bdn;
+                               return dn;
+                       }
+                       return null;
+               }
+
+               private void initialiseDnCmb(Combo combo) {
+                       Map<String, String> dns = userAdminWrapper.getKnownBaseDns(true);
+                       if (dns.isEmpty())
+                               throw new CmsException(
+                                               "No writable base dn found. Cannot create group");
+                       combo.setItems(dns.keySet().toArray(new String[0]));
+                       if (dns.size() == 1)
+                               combo.select(0);
+               }
+       }
+
+       private Combo createGridLC(Composite parent, String label) {
+               Label lbl = new Label(parent, SWT.LEAD);
+               lbl.setText(label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY);
+               combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+               return combo;
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/NewUser.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/NewUser.java
new file mode 100644 (file)
index 0000000..c04c835
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.commands;
+
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.osgi.useradmin.LdifName;
+import org.argeo.osgi.useradmin.UserAdminConf;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.argeo.security.ui.admin.internal.UserAdminWrapper;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Open a wizard that enables creation of a new user. */
+public class NewUser extends AbstractHandler {
+       /**
+        * Email addresses must match this regexp pattern ({@value #EMAIL_PATTERN}.
+        * Thanks to <a href=
+        * "http://www.mkyong.com/regular-expressions/how-to-validate-email-address-with-regular-expression/"
+        * >this tip</a>.
+        */
+       public final static String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
+       // private final static Log log = LogFactory.getLog(NewUser.class);
+       public final static String ID = SecurityAdminPlugin.PLUGIN_ID + ".newUser";
+
+       /* DEPENDENCY INJECTION */
+       private UserAdminWrapper userAdminWrapper;
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               NewUserWizard newUserWizard = new NewUserWizard();
+               newUserWizard.setWindowTitle("User creation");
+               WizardDialog dialog = new WizardDialog(
+                               HandlerUtil.getActiveShell(event), newUserWizard);
+               dialog.open();
+               return null;
+       }
+
+       private class NewUserWizard extends Wizard {
+
+               // pages
+               private MainUserInfoWizardPage mainUserInfo;
+
+               // End user fields
+               private Text dNameTxt, usernameTxt, firstNameTxt, lastNameTxt,
+                               primaryMailTxt, pwd1Txt, pwd2Txt;
+               private Combo baseDnCmb;
+
+               public NewUserWizard() {
+
+               }
+
+               @Override
+               public void addPages() {
+                       mainUserInfo = new MainUserInfoWizardPage();
+                       addPage(mainUserInfo);
+                       String message = "Default wizard that also eases user creation tests:\n "
+                                       + "Mail and last name are automatically "
+                                       + "generated form the uid. Password are defauted to 'demo'.";
+                       mainUserInfo.setMessage(message, WizardPage.WARNING);
+               }
+
+               @SuppressWarnings({ "rawtypes", "unchecked" })
+               @Override
+               public boolean performFinish() {
+                       if (!canFinish())
+                               return false;
+                       String username = mainUserInfo.getUsername();
+                       userAdminWrapper.beginTransactionIfNeeded();
+                       try {
+                               User user = (User) userAdminWrapper.getUserAdmin().createRole(
+                                               getDn(username), Role.USER);
+
+                               Dictionary props = user.getProperties();
+
+                               String lastNameStr = lastNameTxt.getText();
+                               if (EclipseUiUtils.notEmpty(lastNameStr))
+                                       props.put(LdifName.sn.name(), lastNameStr);
+
+                               String firstNameStr = firstNameTxt.getText();
+                               if (EclipseUiUtils.notEmpty(firstNameStr))
+                                       props.put(LdifName.givenName.name(), firstNameStr);
+
+                               String cn = UserAdminUtils.buildDefaultCn(firstNameStr,
+                                               lastNameStr);
+                               if (EclipseUiUtils.notEmpty(cn))
+                                       props.put(LdifName.cn.name(), cn);
+
+                               String mailStr = primaryMailTxt.getText();
+                               if (EclipseUiUtils.notEmpty(mailStr))
+                                       props.put(LdifName.mail.name(), mailStr);
+
+                               char[] password = mainUserInfo.getPassword();
+                               user.getCredentials().put(null, password);
+                               userAdminWrapper.commitOrNotifyTransactionStateChange();
+                               userAdminWrapper.notifyListeners(new UserAdminEvent(null,
+                                               UserAdminEvent.ROLE_CREATED, user));
+                               return true;
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Cannot create new user " + username, e);
+                               return false;
+                       }
+               }
+
+               private class MainUserInfoWizardPage extends WizardPage implements
+                               ModifyListener, ArgeoNames {
+                       private static final long serialVersionUID = -3150193365151601807L;
+
+                       public MainUserInfoWizardPage() {
+                               super("Main");
+                               setTitle("Required Information");
+                       }
+
+                       @Override
+                       public void createControl(Composite parent) {
+                               Composite composite = new Composite(parent, SWT.NONE);
+                               composite.setLayout(new GridLayout(2, false));
+                               dNameTxt = EclipseUiUtils.createGridLT(composite,
+                                               "Distinguished name", this);
+                               dNameTxt.setEnabled(false);
+
+                               baseDnCmb = createGridLC(composite, "Base DN");
+                               initialiseDnCmb(baseDnCmb);
+                               baseDnCmb.addModifyListener(this);
+                               baseDnCmb.addModifyListener(new ModifyListener() {
+                                       private static final long serialVersionUID = -1435351236582736843L;
+
+                                       @Override
+                                       public void modifyText(ModifyEvent event) {
+                                               String name = usernameTxt.getText();
+                                               dNameTxt.setText(getDn(name));
+                                       }
+                               });
+
+                               usernameTxt = EclipseUiUtils.createGridLT(composite,
+                                               "Local ID", this);
+                               usernameTxt.addModifyListener(new ModifyListener() {
+                                       private static final long serialVersionUID = -1435351236582736843L;
+
+                                       @Override
+                                       public void modifyText(ModifyEvent event) {
+                                               String name = usernameTxt.getText();
+                                               if (name.trim().equals("")) {
+                                                       dNameTxt.setText("");
+                                                       lastNameTxt.setText("");
+                                                       primaryMailTxt.setText("");
+                                                       pwd1Txt.setText("");
+                                                       pwd2Txt.setText("");
+                                               } else {
+                                                       dNameTxt.setText(getDn(name));
+                                                       lastNameTxt.setText(name.toUpperCase());
+                                                       primaryMailTxt.setText(getMail(name));
+                                                       pwd1Txt.setText("demo");
+                                                       pwd2Txt.setText("demo");
+                                               }
+                                       }
+                               });
+
+                               primaryMailTxt = EclipseUiUtils.createGridLT(composite,
+                                               "Email", this);
+                               firstNameTxt = EclipseUiUtils.createGridLT(composite,
+                                               "First name", this);
+                               lastNameTxt = EclipseUiUtils.createGridLT(composite,
+                                               "Last name", this);
+                               pwd1Txt = EclipseUiUtils.createGridLP(composite, "Password",
+                                               this);
+                               pwd2Txt = EclipseUiUtils.createGridLP(composite,
+                                               "Repeat password", this);
+                               setControl(composite);
+
+                               // Initialize buttons
+                               setPageComplete(false);
+                               getContainer().updateButtons();
+                       }
+
+                       @Override
+                       public void modifyText(ModifyEvent event) {
+                               String message = checkComplete();
+                               if (message != null) {
+                                       setMessage(message, WizardPage.ERROR);
+                                       setPageComplete(false);
+                               } else {
+                                       setMessage("Complete", WizardPage.INFORMATION);
+                                       setPageComplete(true);
+                               }
+                               getContainer().updateButtons();
+                       }
+
+                       /** @return error message or null if complete */
+                       protected String checkComplete() {
+                               String name = usernameTxt.getText();
+
+                               if (name.trim().equals(""))
+                                       return "User name must not be empty";
+                               Role role = userAdminWrapper.getUserAdmin()
+                                               .getRole(getDn(name));
+                               if (role != null)
+                                       return "User " + name + " already exists";
+                               if (!primaryMailTxt.getText().matches(EMAIL_PATTERN))
+                                       return "Not a valid email address";
+                               if (lastNameTxt.getText().trim().equals(""))
+                                       return "Specify a last name";
+                               if (pwd1Txt.getText().trim().equals(""))
+                                       return "Specify a password";
+                               if (pwd2Txt.getText().trim().equals(""))
+                                       return "Repeat the password";
+                               if (!pwd2Txt.getText().equals(pwd1Txt.getText()))
+                                       return "Passwords are different";
+                               return null;
+                       }
+
+                       @Override
+                       public void setVisible(boolean visible) {
+                               super.setVisible(visible);
+                               if (visible)
+                                       if (baseDnCmb.getSelectionIndex() == -1)
+                                               baseDnCmb.setFocus();
+                                       else
+                                               usernameTxt.setFocus();
+                       }
+
+                       public String getUsername() {
+                               return usernameTxt.getText();
+                       }
+
+                       public char[] getPassword() {
+                               return pwd1Txt.getTextChars();
+                       }
+
+               }
+
+               private Map<String, String> getDns() {
+                       return userAdminWrapper.getKnownBaseDns(true);
+               }
+
+               private String getDn(String uid) {
+                       Map<String, String> dns = getDns();
+                       String bdn = baseDnCmb.getText();
+                       if (EclipseUiUtils.notEmpty(bdn)) {
+                               Dictionary<String, ?> props = UserAdminConf.uriAsProperties(dns
+                                               .get(bdn));
+                               String dn = LdifName.uid.name() + "=" + uid + ","
+                                               + UserAdminConf.userBase.getValue(props) + "," + bdn;
+                               return dn;
+                       }
+                       return null;
+               }
+
+               private void initialiseDnCmb(Combo combo) {
+                       Map<String, String> dns = userAdminWrapper.getKnownBaseDns(true);
+                       if (dns.isEmpty())
+                               throw new CmsException(
+                                               "No writable base dn found. Cannot create user");
+                       combo.setItems(dns.keySet().toArray(new String[0]));
+                       if (dns.size() == 1)
+                               combo.select(0);
+               }
+
+               private String getMail(String username) {
+                       if (baseDnCmb.getSelectionIndex() == -1)
+                               return null;
+                       String baseDn = baseDnCmb.getText();
+                       try {
+                               LdapName name = new LdapName(baseDn);
+                               List<Rdn> rdns = name.getRdns();
+                               return username + "@" + (String) rdns.get(1).getValue() + '.'
+                                               + (String) rdns.get(0).getValue();
+                       } catch (InvalidNameException e) {
+                               throw new CmsException("Unable to generate mail for "
+                                               + username + " with base dn " + baseDn, e);
+                       }
+               }
+       }
+
+       private Combo createGridLC(Composite parent, String label) {
+               Label lbl = new Label(parent, SWT.LEAD);
+               lbl.setText(label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY);
+               combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+               return combo;
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/SaveArgeoUser.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/SaveArgeoUser.java
new file mode 100644 (file)
index 0000000..61d8a7d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.commands;
+
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/** Save the currently edited Argeo user. */
+public class SaveArgeoUser extends AbstractHandler {
+       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
+                       + ".saveArgeoUser";
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               try {
+                       IWorkbenchPart iwp = HandlerUtil.getActiveWorkbenchWindow(event)
+                                       .getActivePage().getActivePart();
+                       if (!(iwp instanceof IEditorPart))
+                               return null;
+                       IEditorPart editor = (IEditorPart) iwp;
+                       editor.doSave(null);
+               } catch (Exception e) {
+                       MessageDialog.openError(Display.getDefault().getActiveShell(),
+                                       "Error", "Cannot save user: " + e.getMessage());
+               }
+               return null;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/UserBatchUpdate.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/UserBatchUpdate.java
new file mode 100644 (file)
index 0000000..c02f5c2
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.commands;
+
+import org.argeo.security.ui.admin.internal.UserAdminWrapper;
+import org.argeo.security.ui.admin.internal.parts.UserBatchUpdateWizard;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/** Launch a wizard to perform batch process on users */
+public class UserBatchUpdate extends AbstractHandler {
+
+       /* DEPENDENCY INJECTION */
+       private UserAdminWrapper uaWrapper;
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               UserBatchUpdateWizard wizard = new UserBatchUpdateWizard(uaWrapper);
+               wizard.setWindowTitle("User batch processing");
+               WizardDialog dialog = new WizardDialog(
+                               HandlerUtil.getActiveShell(event), wizard);
+               dialog.open();
+               return null;
+       }
+
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.uaWrapper = userAdminWrapper;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/UserTransactionHandler.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/commands/UserTransactionHandler.java
new file mode 100644 (file)
index 0000000..236584c
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.commands;
+
+import javax.transaction.Status;
+import javax.transaction.UserTransaction;
+
+import org.argeo.cms.CmsException;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.argeo.security.ui.admin.internal.UiAdminUtils;
+import org.argeo.security.ui.admin.internal.UserAdminWrapper;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Manage the transaction that is bound to the current perspective */
+public class UserTransactionHandler extends AbstractHandler {
+       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
+                       + ".userTransactionHandler";
+
+       public final static String PARAM_COMMAND_ID = "param.commandId";
+
+       public final static String TRANSACTION_BEGIN = "transaction.begin";
+       public final static String TRANSACTION_COMMIT = "transaction.commit";
+       public final static String TRANSACTION_ROLLBACK = "transaction.rollback";
+
+       /* DEPENDENCY INJECTION */
+       private UserAdminWrapper userAdminWrapper;
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               String commandId = event.getParameter(PARAM_COMMAND_ID);
+               final UserTransaction userTransaction = userAdminWrapper
+                               .getUserTransaction();
+               try {
+                       if (TRANSACTION_BEGIN.equals(commandId)) {
+                               if (userTransaction.getStatus() != Status.STATUS_NO_TRANSACTION)
+                                       throw new CmsException("A transaction already exists");
+                               else
+                                       userTransaction.begin();
+                       } else if (TRANSACTION_COMMIT.equals(commandId)) {
+                               if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION)
+                                       throw new CmsException("No transaction.");
+                               else
+                                       userTransaction.commit();
+                       } else if (TRANSACTION_ROLLBACK.equals(commandId)) {
+                               if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION)
+                                       throw new CmsException("No transaction to rollback.");
+                               else {
+                                       userTransaction.rollback();
+                                       userAdminWrapper.notifyListeners(new UserAdminEvent(null,
+                                                       UserAdminEvent.ROLE_CHANGED, null));
+                               }
+                       }
+
+                       UiAdminUtils.notifyTransactionStateChange(userTransaction);
+                       // Try to remove invalid thread access errors when managing users.
+                       // HandlerUtil.getActivePart(event).getSite().getShell().getDisplay()
+                       // .asyncExec(new Runnable() {
+                       // @Override
+                       // public void run() {
+                       // UiAdminUtils
+                       // .notifyTransactionStateChange(userTransaction);
+                       // }
+                       // });
+
+               } catch (CmsException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new CmsException("Unable to call " + commandId + " on "
+                                       + userTransaction, e);
+               }
+               return null;
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/ArgeoUserEditorInput.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/ArgeoUserEditorInput.java
new file mode 100644 (file)
index 0000000..cca2a12
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.parts;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IPersistableElement;
+
+/** Editor input for an Argeo user. */
+public class ArgeoUserEditorInput implements IEditorInput {
+       private final String username;
+
+       public ArgeoUserEditorInput(String username) {
+               this.username = username;
+       }
+
+       public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+               return null;
+       }
+
+       public boolean exists() {
+               return username != null;
+       }
+
+       public ImageDescriptor getImageDescriptor() {
+               return null;
+       }
+
+       public String getName() {
+               return username != null ? username : "<new user>";
+       }
+
+       public IPersistableElement getPersistable() {
+               return null;
+       }
+
+       public String getToolTipText() {
+               return username != null ? username : "<new user>";
+       }
+
+       public boolean equals(Object obj) {
+               if (!(obj instanceof ArgeoUserEditorInput))
+                       return false;
+               if (((ArgeoUserEditorInput) obj).getUsername() == null)
+                       return false;
+               return ((ArgeoUserEditorInput) obj).getUsername().equals(username);
+       }
+
+       public String getUsername() {
+               return username;
+       }
+}
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/DefaultUserMainPage.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/DefaultUserMainPage.java
new file mode 100644 (file)
index 0000000..6529efe
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.parts;
+
+import java.util.Arrays;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.jcr.ArgeoNames;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.AbstractFormPart;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.SectionPart;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.FormPage;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.Section;
+
+/** Display/edit the properties common to all Argeo users */
+public class DefaultUserMainPage extends FormPage implements ArgeoNames {
+       final static String ID = "argeoUserEditor.mainPage";
+
+       private final static Log log = LogFactory.getLog(DefaultUserMainPage.class);
+       private Node userProfile;
+
+       private char[] newPassword;
+
+       public DefaultUserMainPage(FormEditor editor, Node userProfile) {
+               super(editor, ID, "Main");
+               this.userProfile = userProfile;
+       }
+
+       protected void createFormContent(final IManagedForm mf) {
+               try {
+                       ScrolledForm form = mf.getForm();
+                       refreshFormTitle(form);
+                       GridLayout mainLayout = new GridLayout(1, true);
+                       form.getBody().setLayout(mainLayout);
+
+                       createGeneralPart(form.getBody());
+                       createPassworPart(form.getBody());
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot create form content", e);
+               }
+       }
+
+       /** Creates the general section */
+       protected void createGeneralPart(Composite parent)
+                       throws RepositoryException {
+               FormToolkit tk = getManagedForm().getToolkit();
+               Section section = tk.createSection(parent, Section.TITLE_BAR);
+               section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               section.setText("General");
+               Composite body = tk.createComposite(section, SWT.WRAP);
+               section.setClient(body);
+               GridLayout layout = new GridLayout(2, false);
+               body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               body.setLayout(layout);
+
+               final Text commonName = createLT(body, "Displayed Name",
+                               getProperty(Property.JCR_TITLE));
+               final Text firstName = createLT(body, "First name",
+                               getProperty(ARGEO_FIRST_NAME));
+               final Text lastName = createLT(body, "Last name",
+                               getProperty(ARGEO_LAST_NAME));
+               final Text email = createLT(body, "Email",
+                               getProperty(ARGEO_PRIMARY_EMAIL));
+               final Text description = createLMT(body, "Description",
+                               getProperty(Property.JCR_DESCRIPTION));
+
+               // create form part (controller)
+               AbstractFormPart part = new SectionPart(section) {
+                       public void commit(boolean onSave) {
+                               try {
+                                       userProfile.getSession().getWorkspace().getVersionManager()
+                                                       .checkout(userProfile.getPath());
+                                       userProfile.setProperty(Property.JCR_TITLE,
+                                                       commonName.getText());
+                                       userProfile.setProperty(ARGEO_FIRST_NAME,
+                                                       firstName.getText());
+                                       userProfile
+                                                       .setProperty(ARGEO_LAST_NAME, lastName.getText());
+                                       userProfile.setProperty(ARGEO_PRIMARY_EMAIL,
+                                                       email.getText());
+                                       userProfile.setProperty(Property.JCR_DESCRIPTION,
+                                                       description.getText());
+                                       userProfile.getSession().save();
+                                       userProfile.getSession().getWorkspace().getVersionManager()
+                                                       .checkin(userProfile.getPath());
+                                       super.commit(onSave);
+                                       refreshFormTitle(getManagedForm().getForm());
+                                       if (log.isTraceEnabled())
+                                               log.trace("General part committed");
+                               } catch (RepositoryException e) {
+                                       throw new CmsException("Cannot commit", e);
+                               }
+                       }
+               };
+               // if (username != null)
+               // username.addModifyListener(new FormPartML(part));
+               commonName.addModifyListener(new FormPartML(part));
+               firstName.addModifyListener(new FormPartML(part));
+               lastName.addModifyListener(new FormPartML(part));
+               email.addModifyListener(new FormPartML(part));
+               description.addModifyListener(new FormPartML(part));
+               getManagedForm().addPart(part);
+       }
+
+       private void refreshFormTitle(ScrolledForm form) throws RepositoryException {
+               form.setText(getProperty(Property.JCR_TITLE)
+                               + (userProfile.getProperty(ARGEO_ENABLED).getBoolean() ? ""
+                                               : " [DISABLED]"));
+       }
+
+       /** @return the property, or the empty string if not set */
+       protected String getProperty(String name) throws RepositoryException {
+               return userProfile.hasProperty(name) ? userProfile.getProperty(name)
+                               .getString() : "";
+       }
+
+       /** Creates the password section */
+       protected void createPassworPart(Composite parent) {
+               FormToolkit tk = getManagedForm().getToolkit();
+               Section section = tk.createSection(parent, Section.TITLE_BAR);
+               section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               section.setText("Password");
+
+               Composite body = tk.createComposite(section, SWT.WRAP);
+               section.setClient(body);
+               GridLayout layout = new GridLayout(2, false);
+               body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               body.setLayout(layout);
+
+               // add widgets (view)
+               final Text password1 = createLP(body, "New password", "");
+               final Text password2 = createLP(body, "Repeat password", "");
+               // create form part (controller)
+               AbstractFormPart part = new SectionPart(section) {
+
+                       public void commit(boolean onSave) {
+                               if (!password1.getText().equals("")
+                                               || !password2.getText().equals("")) {
+                                       if (password1.getText().equals(password2.getText())) {
+                                               newPassword = password1.getText().toCharArray();
+                                               password1.setText("");
+                                               password2.setText("");
+                                               super.commit(onSave);
+                                       } else {
+                                               password1.setText("");
+                                               password2.setText("");
+                                               throw new CmsException("Passwords are not equals");
+                                       }
+                               }
+                       }
+
+               };
+               password1.addModifyListener(new FormPartML(part));
+               password2.addModifyListener(new FormPartML(part));
+               getManagedForm().addPart(part);
+       }
+
+       /** Creates label and text. */
+       protected Text createLT(Composite body, String label, String value) {
+               FormToolkit toolkit = getManagedForm().getToolkit();
+               Label lbl = toolkit.createLabel(body, label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Text text = toolkit.createText(body, value, SWT.BORDER);
+               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+               return text;
+       }
+
+       /** Creates label and multiline text. */
+       protected Text createLMT(Composite body, String label, String value) {
+               FormToolkit toolkit = getManagedForm().getToolkit();
+               Label lbl = toolkit.createLabel(body, label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Text text = toolkit.createText(body, value, SWT.BORDER | SWT.MULTI);
+               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
+               return text;
+       }
+
+       /** Creates label and password. */
+       protected Text createLP(Composite body, String label, String value) {
+               FormToolkit toolkit = getManagedForm().getToolkit();
+               Label lbl = toolkit.createLabel(body, label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Text text = toolkit.createText(body, value, SWT.BORDER | SWT.PASSWORD);
+               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+               return text;
+       }
+
+       private class FormPartML implements ModifyListener {
+               private static final long serialVersionUID = 6299808129505381333L;
+               private AbstractFormPart formPart;
+
+               public FormPartML(AbstractFormPart generalPart) {
+                       this.formPart = generalPart;
+               }
+
+               public void modifyText(ModifyEvent e) {
+                       formPart.markDirty();
+               }
+       }
+
+       public String getNewPassword() {
+               if (newPassword != null)
+                       return new String(newPassword);
+               else
+                       return null;
+       }
+
+       public void resetNewPassword() {
+               if (newPassword != null)
+                       Arrays.fill(newPassword, 'x');
+               newPassword = null;
+       }
+}
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/GroupMainPage.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/GroupMainPage.java
new file mode 100644 (file)
index 0000000..fe56679
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.parts;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.transaction.UserTransaction;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.parts.LdifUsersTable;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.osgi.useradmin.LdifName;
+import org.argeo.security.ui.admin.SecurityAdminImages;
+import org.argeo.security.ui.admin.internal.UserAdminWrapper;
+import org.argeo.security.ui.admin.internal.parts.UserEditor.GroupChangeListener;
+import org.argeo.security.ui.admin.internal.parts.UserEditor.MainInfoListener;
+import org.argeo.security.ui.admin.internal.providers.CommonNameLP;
+import org.argeo.security.ui.admin.internal.providers.MailLP;
+import org.argeo.security.ui.admin.internal.providers.RoleIconLP;
+import org.argeo.security.ui.admin.internal.providers.UserFilter;
+import org.argeo.security.ui.admin.internal.providers.UserNameLP;
+import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.ViewerDropAdapter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.dnd.TransferData;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.ui.forms.AbstractFormPart;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.SectionPart;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.FormPage;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.Section;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Display/edit main properties of a given group */
+public class GroupMainPage extends FormPage implements ArgeoNames {
+       final static String ID = "GroupEditor.mainPage";
+
+       private final UserEditor editor;
+       private UserAdminWrapper userAdminWrapper;
+
+       // Local configuration
+       private final int PRE_TITLE_INDENT = 10;
+
+       public GroupMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) {
+               super(editor, ID, "Main");
+               this.editor = (UserEditor) editor;
+               this.userAdminWrapper = userAdminWrapper;
+       }
+
+       protected void createFormContent(final IManagedForm mf) {
+               ScrolledForm form = mf.getForm();
+               Composite body = form.getBody();
+               GridLayout mainLayout = new GridLayout();
+               body.setLayout(mainLayout);
+               Group group = (Group) editor.getDisplayedUser();
+               appendOverviewPart(body, group);
+               appendMembersPart(body, group);
+       }
+
+       /** Creates the general section */
+       protected void appendOverviewPart(final Composite parent, final Group group) {
+               FormToolkit tk = getManagedForm().getToolkit();
+               Composite body = addSection(tk, parent, "Main information");
+               GridLayout layout = new GridLayout(2, false);
+               body.setLayout(layout);
+
+               final Text dnTxt = createLT(body, "DN", group.getName());
+               dnTxt.setEnabled(false);
+
+               final Text cnTxt = createLT(body, "Common Name",
+                               UserAdminUtils.getProperty(group, LdifName.cn.name()));
+               cnTxt.setEnabled(false);
+
+               Label descLbl = new Label(body, SWT.LEAD);
+               descLbl.setText("Description");
+               descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));
+               final Text descTxt = new Text(body, SWT.LEAD | SWT.MULTI | SWT.WRAP
+                               | SWT.BORDER);
+               GridData gd = EclipseUiUtils.fillAll();
+               gd.heightHint = 100;
+               descTxt.setLayoutData(gd);
+
+               // create form part (controller)
+               AbstractFormPart part = new SectionPart((Section) body.getParent()) {
+
+                       private MainInfoListener listener;
+
+                       @Override
+                       public void initialize(IManagedForm form) {
+                               super.initialize(form);
+                               listener = editor.new MainInfoListener(parent.getDisplay(),
+                                               this);
+                               userAdminWrapper.addListener(listener);
+                       }
+
+                       @Override
+                       public void dispose() {
+                               userAdminWrapper.removeListener(listener);
+                               super.dispose();
+                       }
+
+                       @SuppressWarnings("unchecked")
+                       public void commit(boolean onSave) {
+                               group.getProperties().put(LdifName.description.name(),
+                                               descTxt.getText());
+                               // Enable common name ?
+                               // editor.setProperty(UserAdminConstants.KEY_CN,
+                               // email.getText());
+                               super.commit(onSave);
+                       }
+
+                       @Override
+                       public void refresh() {
+                               refreshFormTitle(group);
+                               dnTxt.setText(group.getName());
+                               cnTxt.setText(UserAdminUtils.getProperty(group,
+                                               LdifName.cn.name()));
+                               descTxt.setText(UserAdminUtils.getProperty(group,
+                                               LdifName.description.name()));
+                               super.refresh();
+                       }
+               };
+
+               ModifyListener defaultListener = editor.new FormPartML(part);
+               descTxt.addModifyListener(defaultListener);
+               getManagedForm().addPart(part);
+       }
+
+       /** Filtered table with members. Has drag & drop ability */
+       protected void appendMembersPart(Composite parent, Group group) {
+
+               FormToolkit tk = getManagedForm().getToolkit();
+               Section section = tk.createSection(parent, Section.TITLE_BAR);
+               section.setLayoutData(EclipseUiUtils.fillAll());
+
+               Composite body = new Composite(section, SWT.NO_FOCUS);
+               section.setClient(body);
+               body.setLayoutData(EclipseUiUtils.fillAll());
+
+               LdifUsersTable userTableViewerCmp = createMemberPart(body, group);
+
+               SectionPart part = new GroupMembersPart(section, userTableViewerCmp,
+                               group);
+               getManagedForm().addPart(part);
+               addRemoveAbitily(part, userTableViewerCmp.getTableViewer(), group);
+       }
+
+       public LdifUsersTable createMemberPart(Composite parent, Group group) {
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+               // Define the displayed columns
+               List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+               columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24));
+               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name",
+                               150));
+               columnDefs.add(new ColumnDefinition(new MailLP(), "Primary Mail", 150));
+               columnDefs.add(new ColumnDefinition(new UserNameLP(),
+                               "Distinguished Name", 240));
+
+               // Create and configure the table
+               LdifUsersTable userViewerCmp = new MyUserTableViewer(parent, SWT.MULTI
+                               | SWT.H_SCROLL | SWT.V_SCROLL, userAdminWrapper.getUserAdmin());
+
+               userViewerCmp.setColumnDefinitions(columnDefs);
+               userViewerCmp.populate(true, false);
+               userViewerCmp.setLayoutData(EclipseUiUtils.fillAll());
+
+               // Controllers
+               TableViewer userViewer = userViewerCmp.getTableViewer();
+               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener());
+               int operations = DND.DROP_COPY | DND.DROP_MOVE;
+               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
+               userViewer.addDropSupport(operations, tt,
+                               new GroupDropListener(userAdminWrapper, userViewerCmp,
+                                               (Group) editor.getDisplayedUser()));
+
+               return userViewerCmp;
+       }
+
+       // Local viewers
+       private class MyUserTableViewer extends LdifUsersTable {
+               private static final long serialVersionUID = 8467999509931900367L;
+
+               private final UserFilter userFilter;
+
+               public MyUserTableViewer(Composite parent, int style,
+                               UserAdmin userAdmin) {
+                       super(parent, style, true);
+                       userFilter = new UserFilter();
+
+               }
+
+               @Override
+               protected List<User> listFilteredElements(String filter) {
+                       Group group = (Group) editor.getDisplayedUser();
+                       Role[] roles = group.getMembers();
+                       List<User> users = new ArrayList<User>();
+                       userFilter.setSearchText(filter);
+                       for (Role role : roles)
+                               // if (role.getType() == Role.GROUP)
+                               if (userFilter.select(null, null, role))
+                                       users.add((User) role);
+                       return users;
+               }
+       }
+
+       private void addRemoveAbitily(SectionPart sectionPart,
+                       TableViewer userViewer, Group group) {
+               Section section = sectionPart.getSection();
+               ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
+               ToolBar toolbar = toolBarManager.createControl(section);
+               final Cursor handCursor = new Cursor(section.getDisplay(),
+                               SWT.CURSOR_HAND);
+               toolbar.setCursor(handCursor);
+               toolbar.addDisposeListener(new DisposeListener() {
+                       private static final long serialVersionUID = 3882131405820522925L;
+
+                       public void widgetDisposed(DisposeEvent e) {
+                               if ((handCursor != null) && (handCursor.isDisposed() == false)) {
+                                       handCursor.dispose();
+                               }
+                       }
+               });
+
+               Action action = new RemoveMembershipAction(userViewer, group,
+                               "Remove selected items from this group",
+                               SecurityAdminImages.ICON_REMOVE_DESC);
+               toolBarManager.add(action);
+               toolBarManager.update(true);
+               section.setTextClient(toolbar);
+       }
+
+       private class RemoveMembershipAction extends Action {
+               private static final long serialVersionUID = -1337713097184522588L;
+
+               private final TableViewer userViewer;
+               private final Group group;
+
+               RemoveMembershipAction(TableViewer userViewer, Group group,
+                               String name, ImageDescriptor img) {
+                       super(name, img);
+                       this.userViewer = userViewer;
+                       this.group = group;
+               }
+
+               @Override
+               public void run() {
+                       ISelection selection = userViewer.getSelection();
+                       if (selection.isEmpty())
+                               return;
+
+                       @SuppressWarnings("unchecked")
+                       Iterator<User> it = ((IStructuredSelection) selection).iterator();
+                       List<User> users = new ArrayList<User>();
+                       while (it.hasNext()) {
+                               User currUser = it.next();
+                               users.add(currUser);
+                       }
+
+                       userAdminWrapper.beginTransactionIfNeeded();
+                       for (User user : users) {
+                               group.removeMember(user);
+                       }
+                       userAdminWrapper.commitOrNotifyTransactionStateChange();
+                       userAdminWrapper.notifyListeners(new UserAdminEvent(null,
+                                       UserAdminEvent.ROLE_CHANGED, group));
+               }
+       }
+
+       // LOCAL CONTROLLERS
+       private class GroupMembersPart extends SectionPart {
+               private final LdifUsersTable userViewer;
+               private final Group group;
+
+               private GroupChangeListener listener;
+
+               public GroupMembersPart(Section section, LdifUsersTable userViewer,
+                               Group group) {
+                       super(section);
+                       this.userViewer = userViewer;
+                       this.group = group;
+               }
+
+               @Override
+               public void initialize(IManagedForm form) {
+                       super.initialize(form);
+                       listener = editor.new GroupChangeListener(userViewer.getDisplay(),
+                                       GroupMembersPart.this);
+                       userAdminWrapper.addListener(listener);
+               }
+
+               @Override
+               public void dispose() {
+                       userAdminWrapper.removeListener(listener);
+                       super.dispose();
+               }
+
+               @Override
+               public void refresh() {
+                       refreshFormTitle(group);
+                       getSection().setText(
+                                       "Members of group "
+                                                       + UserAdminUtils.getProperty(group,
+                                                                       LdifName.cn.name()));
+                       userViewer.refresh();
+                       super.refresh();
+               }
+       }
+
+       /**
+        * Defines this table as being a potential target to add group membership
+        * (roles) to this group
+        */
+       private class GroupDropListener extends ViewerDropAdapter {
+               private static final long serialVersionUID = 2893468717831451621L;
+
+               private final UserAdminWrapper userAdminWrapper;
+               // private final LdifUsersTable myUserViewerCmp;
+               private final Group myGroup;
+
+               public GroupDropListener(UserAdminWrapper userAdminWrapper,
+                               LdifUsersTable userTableViewerCmp, Group group) {
+                       super(userTableViewerCmp.getTableViewer());
+                       this.userAdminWrapper = userAdminWrapper;
+                       this.myGroup = group;
+                       // this.myUserViewerCmp = userTableViewerCmp;
+               }
+
+               @Override
+               public boolean validateDrop(Object target, int operation,
+                               TransferData transferType) {
+                       // Target is always OK in a list only view
+                       // TODO check if not a string
+                       boolean validDrop = true;
+                       return validDrop;
+               }
+
+               @Override
+               public void drop(DropTargetEvent event) {
+                       // TODO Is there an opportunity to perform the check before?
+
+                       String newUserName = (String) event.data;
+                       UserAdmin myUserAdmin = userAdminWrapper.getUserAdmin();
+                       Role role = myUserAdmin.getRole(newUserName);
+                       if (role.getType() == Role.GROUP) {
+                               Group newGroup = (Group) role;
+                               Shell shell = getViewer().getControl().getShell();
+                               // Sanity checks
+                               if (myGroup == newGroup) { // Equality
+                                       MessageDialog.openError(shell, "Forbidden addition ",
+                                                       "A group cannot be a member of itself.");
+                                       return;
+                               }
+
+                               // Cycle
+                               String myName = myGroup.getName();
+                               List<User> myMemberships = editor.getFlatGroups(myGroup);
+                               if (myMemberships.contains(newGroup)) {
+                                       MessageDialog.openError(shell, "Forbidden addition: cycle",
+                                                       "Cannot add " + newUserName + " to group " + myName
+                                                                       + ". This would create a cycle");
+                                       return;
+                               }
+
+                               // Already member
+                               List<User> newGroupMemberships = editor.getFlatGroups(newGroup);
+                               if (newGroupMemberships.contains(myGroup)) {
+                                       MessageDialog.openError(shell, "Forbidden addition",
+                                                       "Cannot add " + newUserName + " to group " + myName
+                                                                       + ", this membership already exists");
+                                       return;
+                               }
+                               userAdminWrapper.beginTransactionIfNeeded();
+                               myGroup.addMember(newGroup);
+                               userAdminWrapper.commitOrNotifyTransactionStateChange();
+                               userAdminWrapper.notifyListeners(new UserAdminEvent(null,
+                                               UserAdminEvent.ROLE_CHANGED, myGroup));
+                       } else if (role.getType() == Role.USER) {
+                               // TODO check if the group is already member of this group
+                               UserTransaction transaction = userAdminWrapper
+                                               .beginTransactionIfNeeded();
+                               User user = (User) role;
+                               myGroup.addMember(user);
+                               if (UserAdminWrapper.COMMIT_ON_SAVE)
+                                       try {
+                                               transaction.commit();
+                                       } catch (Exception e) {
+                                               throw new CmsException("Cannot commit transaction "
+                                                               + "after user group membership update", e);
+                                       }
+                               userAdminWrapper.notifyListeners(new UserAdminEvent(null,
+                                               UserAdminEvent.ROLE_CHANGED, myGroup));
+                       }
+                       super.drop(event);
+               }
+
+               @Override
+               public boolean performDrop(Object data) {
+                       // myUserViewerCmp.refresh();
+                       return true;
+               }
+       }
+
+       // LOCAL HELPERS
+       private void refreshFormTitle(Group group) {
+               getManagedForm().getForm().setText(
+                               UserAdminUtils.getProperty(group, LdifName.cn.name()));
+       }
+
+       private Composite addSection(FormToolkit tk, Composite parent, String title) {
+               Section section = tk.createSection(parent, Section.TITLE_BAR);
+               GridData gd = EclipseUiUtils.fillWidth();
+               gd.verticalAlignment = PRE_TITLE_INDENT;
+               section.setLayoutData(gd);
+               section.setText(title);
+               Composite body = tk.createComposite(section, SWT.WRAP);
+               body.setLayoutData(EclipseUiUtils.fillAll());
+               section.setClient(body);
+               return body;
+       }
+
+       /** Creates label and text. */
+       private Text createLT(Composite body, String label, String value) {
+               FormToolkit toolkit = getManagedForm().getToolkit();
+               Label lbl = toolkit.createLabel(body, label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Text text = toolkit.createText(body, value, SWT.BORDER);
+               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+               return text;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/GroupsView.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/GroupsView.java
new file mode 100644 (file)
index 0000000..82f4e1b
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.parts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.parts.LdifUsersTable;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.osgi.useradmin.LdifName;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.argeo.security.ui.admin.internal.UiUserAdminListener;
+import org.argeo.security.ui.admin.internal.UserAdminWrapper;
+import org.argeo.security.ui.admin.internal.providers.CommonNameLP;
+import org.argeo.security.ui.admin.internal.providers.DomainNameLP;
+import org.argeo.security.ui.admin.internal.providers.RoleIconLP;
+import org.argeo.security.ui.admin.internal.providers.UserDragListener;
+import org.argeo.security.ui.admin.internal.providers.UserNameLP;
+import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.part.ViewPart;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdminEvent;
+import org.osgi.service.useradmin.UserAdminListener;
+
+/** List all groups with filter */
+public class GroupsView extends ViewPart implements ArgeoNames {
+       private final static Log log = LogFactory.getLog(GroupsView.class);
+       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
+                       + ".groupsView";
+
+       /* DEPENDENCY INJECTION */
+       private UserAdminWrapper userAdminWrapper;
+
+       // UI Objects
+       private LdifUsersTable groupTableViewerCmp;
+       private TableViewer userViewer;
+       private List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+
+       private UserAdminListener listener;
+
+       @Override
+       public void createPartControl(Composite parent) {
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+               boolean isAdmin = UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN);
+
+               // Define the displayed columns
+               columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 26));
+               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name",
+                               150));
+               columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
+               // Only show technical DN to admin
+               if (isAdmin)
+                       columnDefs.add(new ColumnDefinition(new UserNameLP(),
+                                       "Distinguished Name", 300));
+
+               // Create and configure the table
+               groupTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI
+                               | SWT.H_SCROLL | SWT.V_SCROLL);
+
+               groupTableViewerCmp.setColumnDefinitions(columnDefs);
+               if (isAdmin)
+                       groupTableViewerCmp.populateWithStaticFilters(false, false);
+               else
+                       groupTableViewerCmp.populate(true, false);
+
+               groupTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll());
+
+               // Links
+               userViewer = groupTableViewerCmp.getTableViewer();
+               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener());
+               getViewSite().setSelectionProvider(userViewer);
+
+               // Really?
+               groupTableViewerCmp.refresh();
+
+               // Drag and drop
+               int operations = DND.DROP_COPY | DND.DROP_MOVE;
+               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
+               userViewer.addDragSupport(operations, tt, new UserDragListener(
+                               userViewer));
+
+               // // Register a useradmin listener
+               // listener = new UserAdminListener() {
+               // @Override
+               // public void roleChanged(UserAdminEvent event) {
+               // if (userViewer != null && !userViewer.getTable().isDisposed())
+               // refresh();
+               // }
+               // };
+               // userAdminWrapper.addListener(listener);
+               // }
+
+               // Register a useradmin listener
+               listener = new MyUiUAListener(parent.getDisplay());
+               userAdminWrapper.addListener(listener);
+       }
+
+       private class MyUiUAListener extends UiUserAdminListener {
+               public MyUiUAListener(Display display) {
+                       super(display);
+               }
+
+               @Override
+               public void roleChangedToUiThread(UserAdminEvent event) {
+                       if (userViewer != null && !userViewer.getTable().isDisposed())
+                               refresh();
+               }
+       }
+
+       private class MyUserTableViewer extends LdifUsersTable {
+               private static final long serialVersionUID = 8467999509931900367L;
+
+               private boolean showSystemRoles = false;
+
+               private final String[] knownProps = { LdifName.uid.name(),
+                               LdifName.cn.name(), LdifName.dn.name() };
+
+               public MyUserTableViewer(Composite parent, int style) {
+                       super(parent, style);
+               }
+
+               protected void populateStaticFilters(Composite staticFilterCmp) {
+                       staticFilterCmp.setLayout(new GridLayout());
+                       final Button showSystemRoleBtn = new Button(staticFilterCmp,
+                                       SWT.CHECK);
+                       showSystemRoleBtn.setText("Show system roles");
+                       showSystemRoleBtn.addSelectionListener(new SelectionAdapter() {
+                               private static final long serialVersionUID = -7033424592697691676L;
+
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       showSystemRoles = showSystemRoleBtn.getSelection();
+                                       refresh();
+                               }
+
+                       });
+               }
+
+               @Override
+               protected List<User> listFilteredElements(String filter) {
+                       Role[] roles;
+                       try {
+                               StringBuilder builder = new StringBuilder();
+                               StringBuilder tmpBuilder = new StringBuilder();
+                               if (EclipseUiUtils.notEmpty(filter))
+                                       for (String prop : knownProps) {
+                                               tmpBuilder.append("(");
+                                               tmpBuilder.append(prop);
+                                               tmpBuilder.append("=*");
+                                               tmpBuilder.append(filter);
+                                               tmpBuilder.append("*)");
+                                       }
+                               if (tmpBuilder.length() > 1) {
+                                       builder.append("(&(").append(LdifName.objectClass.name())
+                                                       .append("=").append(LdifName.groupOfNames.name())
+                                                       .append(")");
+                                       if (!showSystemRoles)
+                                               builder.append("(!(").append(LdifName.dn.name())
+                                                               .append("=*")
+                                                               .append(AuthConstants.ROLES_BASEDN)
+                                                               .append("))");
+                                       builder.append("(|");
+                                       builder.append(tmpBuilder.toString());
+                                       builder.append("))");
+                               } else {
+                                       if (!showSystemRoles)
+                                               builder.append("(&(")
+                                                               .append(LdifName.objectClass.name())
+                                                               .append("=")
+                                                               .append(LdifName.groupOfNames.name())
+                                                               .append(")(!(").append(LdifName.dn.name())
+                                                               .append("=*")
+                                                               .append(AuthConstants.ROLES_BASEDN)
+                                                               .append(")))");
+                                       else
+                                               builder.append("(").append(LdifName.objectClass.name())
+                                                               .append("=")
+                                                               .append(LdifName.groupOfNames.name())
+                                                               .append(")");
+
+                               }
+                               roles = userAdminWrapper.getUserAdmin().getRoles(
+                                               builder.toString());
+                       } catch (InvalidSyntaxException e) {
+                               throw new CmsException("Unable to get roles with filter: "
+                                               + filter, e);
+                       }
+                       List<User> users = new ArrayList<User>();
+                       for (Role role : roles)
+                               if (!users.contains(role))
+                                       users.add((User) role);
+                               else
+                                       log.warn("Duplicated role: " + role);
+
+                       return users;
+               }
+       }
+
+       public void refresh() {
+               groupTableViewerCmp.refresh();
+       }
+
+       // Override generic view methods
+       @Override
+       public void dispose() {
+               userAdminWrapper.removeListener(listener);
+               super.dispose();
+       }
+
+       @Override
+       public void setFocus() {
+               groupTableViewerCmp.setFocus();
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java
new file mode 100644 (file)
index 0000000..31b2042
--- /dev/null
@@ -0,0 +1,578 @@
+package org.argeo.security.ui.admin.internal.parts;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.transaction.SystemException;
+import javax.transaction.UserTransaction;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.parts.LdifUsersTable;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.osgi.useradmin.LdifName;
+import org.argeo.security.ui.admin.internal.UserAdminWrapper;
+import org.argeo.security.ui.admin.internal.providers.CommonNameLP;
+import org.argeo.security.ui.admin.internal.providers.DomainNameLP;
+import org.argeo.security.ui.admin.internal.providers.MailLP;
+import org.argeo.security.ui.admin.internal.providers.UserNameLP;
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.PageChangedEvent;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+/** Wizard to update users */
+public class UserBatchUpdateWizard extends Wizard {
+
+       private final static Log log = LogFactory
+                       .getLog(UserBatchUpdateWizard.class);
+       private UserAdminWrapper userAdminWrapper;
+
+       // pages
+       private ChooseCommandWizardPage chooseCommandPage;
+       private ChooseUsersWizardPage userListPage;
+       private ValidateAndLaunchWizardPage validatePage;
+
+       // Various implemented commands keys
+       private final static String CMD_UPDATE_PASSWORD = "resetPassword";
+       private final static String CMD_GROUP_MEMBERSHIP = "groupMembership";
+
+       private final Map<String, String> commands = new HashMap<String, String>() {
+               private static final long serialVersionUID = 1L;
+               {
+                       put("Reset password(s)", CMD_UPDATE_PASSWORD);
+                       // TODO implement role / group management
+                       // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP);
+               }
+       };
+
+       public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+
+       @Override
+       public void addPages() {
+               chooseCommandPage = new ChooseCommandWizardPage();
+               addPage(chooseCommandPage);
+               userListPage = new ChooseUsersWizardPage();
+               addPage(userListPage);
+               validatePage = new ValidateAndLaunchWizardPage();
+               addPage(validatePage);
+       }
+
+       @Override
+       public boolean performFinish() {
+               if (!canFinish())
+                       return false;
+               UserTransaction ut = userAdminWrapper.getUserTransaction();
+               try {
+                       if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION
+                                       && !MessageDialog.openConfirm(getShell(),
+                                                       "Existing Transaction",
+                                                       "A user transaction is already existing, "
+                                                                       + "are you sure you want to proceed ?"))
+                               return false;
+               } catch (SystemException e) {
+                       throw new CmsException("Cannot get user transaction state "
+                                       + "before user batch update", e);
+               }
+
+               // We cannot use jobs, user modifications are still meant to be done in
+               // the UIThread
+               // UpdateJob job = null;
+               // if (job != null)
+               // job.schedule();
+
+               if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) {
+                       char[] newValue = chooseCommandPage.getPwdValue();
+                       if (newValue == null)
+                               throw new CmsException(
+                                               "Password cannot be null or an empty string");
+                       ResetPassword job = new ResetPassword(userAdminWrapper,
+                                       userListPage.getSelectedUsers(), newValue);
+                       job.doUpdate();
+               }
+               return true;
+       }
+
+       public boolean canFinish() {
+               if (this.getContainer().getCurrentPage() == validatePage)
+                       return true;
+               return false;
+       }
+
+       private class ResetPassword {
+               private char[] newPwd;
+               private UserAdminWrapper userAdminWrapper;
+               private List<User> usersToUpdate;
+
+               public ResetPassword(UserAdminWrapper userAdminWrapper,
+                               List<User> usersToUpdate, char[] newPwd) {
+                       this.newPwd = newPwd;
+                       this.usersToUpdate = usersToUpdate;
+                       this.userAdminWrapper = userAdminWrapper;
+               }
+
+               @SuppressWarnings("unchecked")
+               protected void doUpdate() {
+                       userAdminWrapper.beginTransactionIfNeeded();
+                       try {
+                               for (User user : usersToUpdate) {
+                                       // the char array is emptied after being used.
+                                       user.getCredentials().put(null, newPwd.clone());
+                               }
+                               userAdminWrapper.commitOrNotifyTransactionStateChange();
+                       } catch (Exception e) {
+                               throw new CmsException("Cannot perform batch update on users",
+                                               e);
+                       } finally {
+                               UserTransaction ut = userAdminWrapper.getUserTransaction();
+                               try {
+                                       if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
+                                               ut.rollback();
+                               } catch (IllegalStateException | SecurityException
+                                               | SystemException e) {
+                                       log.error("Unable to rollback session in 'finally', "
+                                                       + "the system might be in a dirty state");
+                                       e.printStackTrace();
+                               }
+                       }
+               }
+       }
+
+       // @SuppressWarnings("unused")
+       // private class AddToGroup extends UpdateJob {
+       // private String groupID;
+       // private Session session;
+       //
+       // public AddToGroup(Session session, List<Node> nodesToUpdate,
+       // String groupID) {
+       // super(session, nodesToUpdate);
+       // this.session = session;
+       // this.groupID = groupID;
+       // }
+       //
+       // protected void doUpdate(Node node) {
+       // log.info("Add/Remove to group actions are not yet implemented");
+       // // TODO implement this
+       // // try {
+       // // throw new CmsException("Not yet implemented");
+       // // } catch (RepositoryException re) {
+       // // throw new CmsException(
+       // // "Unable to update boolean value for node " + node, re);
+       // // }
+       // }
+       // }
+
+       // /**
+       // * Base privileged job that will be run asynchronously to perform the
+       // batch
+       // * update
+       // */
+       // private abstract class UpdateJob extends PrivilegedJob {
+       //
+       // private final UserAdminWrapper userAdminWrapper;
+       // private final List<User> usersToUpdate;
+       //
+       // protected abstract void doUpdate(User user);
+       //
+       // public UpdateJob(UserAdminWrapper userAdminWrapper,
+       // List<User> usersToUpdate) {
+       // super("Perform update");
+       // this.usersToUpdate = usersToUpdate;
+       // this.userAdminWrapper = userAdminWrapper;
+       // }
+       //
+       // @Override
+       // protected IStatus doRun(IProgressMonitor progressMonitor) {
+       // try {
+       // ArgeoMonitor monitor = new EclipseArgeoMonitor(progressMonitor);
+       // int total = usersToUpdate.size();
+       // monitor.beginTask("Performing change", total);
+       // userAdminWrapper.beginTransactionIfNeeded();
+       // for (User user : usersToUpdate) {
+       // doUpdate(user);
+       // monitor.worked(1);
+       // }
+       // userAdminWrapper.getUserTransaction().commit();
+       // } catch (Exception e) {
+       // throw new CmsException(
+       // "Cannot perform batch update on users", e);
+       // } finally {
+       // UserTransaction ut = userAdminWrapper.getUserTransaction();
+       // try {
+       // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
+       // ut.rollback();
+       // } catch (IllegalStateException | SecurityException
+       // | SystemException e) {
+       // log.error("Unable to rollback session in 'finally', "
+       // + "the system might be in a dirty state");
+       // e.printStackTrace();
+       // }
+       // }
+       // return Status.OK_STATUS;
+       // }
+       // }
+
+       // PAGES
+       /** Displays a combo box that enables user to choose which action to perform */
+       private class ChooseCommandWizardPage extends WizardPage {
+               private static final long serialVersionUID = -8069434295293996633L;
+               private Combo chooseCommandCmb;
+               private Button trueChk;
+               private Text valueTxt;
+               private Text pwdTxt;
+               private Text pwd2Txt;
+
+               public ChooseCommandWizardPage() {
+                       super("Choose a command to run.");
+                       setTitle("Choose a command to run.");
+               }
+
+               @Override
+               public void createControl(Composite parent) {
+                       GridLayout gl = new GridLayout();
+                       Composite container = new Composite(parent, SWT.NO_FOCUS);
+                       container.setLayout(gl);
+
+                       chooseCommandCmb = new Combo(container, SWT.READ_ONLY);
+                       chooseCommandCmb.setLayoutData(EclipseUiUtils.fillWidth());
+                       String[] values = commands.keySet().toArray(new String[0]);
+                       chooseCommandCmb.setItems(values);
+
+                       final Composite bottomPart = new Composite(container, SWT.NO_FOCUS);
+                       bottomPart.setLayoutData(EclipseUiUtils.fillAll());
+                       bottomPart.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+                       chooseCommandCmb.addSelectionListener(new SelectionAdapter() {
+                               private static final long serialVersionUID = 1L;
+
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       if (getCommand().equals(CMD_UPDATE_PASSWORD))
+                                               populatePasswordCmp(bottomPart);
+                                       else if (getCommand().equals(CMD_GROUP_MEMBERSHIP))
+                                               populateGroupCmp(bottomPart);
+                                       else
+                                               populateBooleanFlagCmp(bottomPart);
+                                       checkPageComplete();
+                                       bottomPart.layout(true, true);
+                               }
+                       });
+                       setControl(container);
+               }
+
+               private void populateBooleanFlagCmp(Composite parent) {
+                       EclipseUiUtils.clear(parent);
+                       trueChk = new Button(parent, SWT.CHECK);
+                       trueChk.setText("Do it. (It will to the contrary if unchecked)");
+                       trueChk.setSelection(true);
+                       trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
+               }
+
+               private void populatePasswordCmp(Composite parent) {
+                       EclipseUiUtils.clear(parent);
+                       Composite body = new Composite(parent, SWT.NO_FOCUS);
+
+                       ModifyListener ml = new ModifyListener() {
+                               private static final long serialVersionUID = -1558726363536729634L;
+
+                               @Override
+                               public void modifyText(ModifyEvent event) {
+                                       checkPageComplete();
+                               }
+                       };
+
+                       body.setLayout(new GridLayout(2, false));
+                       body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       pwdTxt = EclipseUiUtils.createGridLP(body, "New password", ml);
+                       pwd2Txt = EclipseUiUtils.createGridLP(body, "Repeat password", ml);
+               }
+
+               private void checkPageComplete() {
+                       String errorMsg = null;
+                       if (chooseCommandCmb.getSelectionIndex() < 0)
+                               errorMsg = "Please select an action";
+                       else if (CMD_UPDATE_PASSWORD.equals(getCommand())) {
+                               if (EclipseUiUtils.isEmpty(pwdTxt.getText())
+                                               || pwdTxt.getText().length() < 4)
+                                       errorMsg = "Please enter a password that is at least 4 character long";
+                               else if (!pwdTxt.getText().equals(pwd2Txt.getText()))
+                                       errorMsg = "Passwords are different";
+                       }
+                       if (EclipseUiUtils.notEmpty(errorMsg)) {
+                               setMessage(errorMsg, WizardPage.ERROR);
+                               setPageComplete(false);
+                       } else {
+                               setMessage("Page complete, you can proceed to user choice",
+                                               WizardPage.INFORMATION);
+                               setPageComplete(true);
+                       }
+
+                       getContainer().updateButtons();
+               }
+
+               private void populateGroupCmp(Composite parent) {
+                       EclipseUiUtils.clear(parent);
+                       trueChk = new Button(parent, SWT.CHECK);
+                       trueChk.setText("Add to group. (It will remove user(s) from the "
+                                       + "corresponding group if unchecked)");
+                       trueChk.setSelection(true);
+                       trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
+               }
+
+               protected String getCommand() {
+                       return commands.get(chooseCommandCmb.getItem(chooseCommandCmb
+                                       .getSelectionIndex()));
+               }
+
+               protected String getCommandLbl() {
+                       return chooseCommandCmb.getItem(chooseCommandCmb
+                                       .getSelectionIndex());
+               }
+
+               @SuppressWarnings("unused")
+               protected boolean getBoleanValue() {
+                       // FIXME this is not consistent and will lead to errors.
+                       if (ArgeoNames.ARGEO_ENABLED.equals(getCommand()))
+                               return trueChk.getSelection();
+                       else
+                               return !trueChk.getSelection();
+               }
+
+               @SuppressWarnings("unused")
+               protected String getStringValue() {
+                       String value = null;
+                       if (valueTxt != null) {
+                               value = valueTxt.getText();
+                               if ("".equals(value.trim()))
+                                       value = null;
+                       }
+                       return value;
+               }
+
+               protected char[] getPwdValue() {
+                       // We do not directly reset the password text fields: There is no
+                       // need to over secure this process: setting a pwd to multi users
+                       // at the same time is anyhow a bad practice and should be used only
+                       // in test environment or for temporary access
+                       if (pwdTxt == null || pwdTxt.isDisposed())
+                               return null;
+                       else
+                               return pwdTxt.getText().toCharArray();
+               }
+       }
+
+       /**
+        * Displays a list of users with a check box to be able to choose some of
+        * them
+        */
+       private class ChooseUsersWizardPage extends WizardPage implements
+                       IPageChangedListener {
+               private static final long serialVersionUID = 7651807402211214274L;
+               private ChooseUserTableViewer userTableCmp;
+
+               public ChooseUsersWizardPage() {
+                       super("Choose Users");
+                       setTitle("Select users who will be impacted");
+               }
+
+               @Override
+               public void createControl(Composite parent) {
+                       Composite pageCmp = new Composite(parent, SWT.NONE);
+                       pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+                       // Define the displayed columns
+                       List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+                       columnDefs.add(new ColumnDefinition(new CommonNameLP(),
+                                       "Common Name", 150));
+                       columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
+                       columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain",
+                                       200));
+
+                       // Only show technical DN to admin
+                       if (UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN))
+                               columnDefs.add(new ColumnDefinition(new UserNameLP(),
+                                               "Distinguished Name", 300));
+
+                       userTableCmp = new ChooseUserTableViewer(pageCmp, SWT.MULTI
+                                       | SWT.H_SCROLL | SWT.V_SCROLL);
+                       userTableCmp.setLayoutData(EclipseUiUtils.fillAll());
+                       userTableCmp.setColumnDefinitions(columnDefs);
+                       userTableCmp.populate(true, true);
+                       userTableCmp.refresh();
+
+                       setControl(pageCmp);
+
+                       // Add listener to update message when shown
+                       final IWizardContainer wContainer = this.getContainer();
+                       if (wContainer instanceof IPageChangeProvider) {
+                               ((IPageChangeProvider) wContainer).addPageChangedListener(this);
+                       }
+
+               }
+
+               @Override
+               public void pageChanged(PageChangedEvent event) {
+                       if (event.getSelectedPage() == this) {
+                               String msg = "Chosen batch action: "
+                                               + chooseCommandPage.getCommandLbl();
+                               ((WizardPage) event.getSelectedPage()).setMessage(msg);
+                       }
+               }
+
+               protected List<User> getSelectedUsers() {
+                       return userTableCmp.getSelectedUsers();
+               }
+
+               private class ChooseUserTableViewer extends LdifUsersTable {
+                       private static final long serialVersionUID = 5080437561015853124L;
+                       private final String[] knownProps = { LdifName.uid.name(),
+                                       LdifName.dn.name(), LdifName.cn.name(),
+                                       LdifName.givenName.name(), LdifName.sn.name(),
+                                       LdifName.mail.name() };
+
+                       public ChooseUserTableViewer(Composite parent, int style) {
+                               super(parent, style);
+                       }
+
+                       @Override
+                       protected List<User> listFilteredElements(String filter) {
+                               Role[] roles;
+
+                               try {
+                                       StringBuilder builder = new StringBuilder();
+
+                                       StringBuilder tmpBuilder = new StringBuilder();
+                                       if (EclipseUiUtils.notEmpty(filter))
+                                               for (String prop : knownProps) {
+                                                       tmpBuilder.append("(");
+                                                       tmpBuilder.append(prop);
+                                                       tmpBuilder.append("=*");
+                                                       tmpBuilder.append(filter);
+                                                       tmpBuilder.append("*)");
+                                               }
+                                       if (tmpBuilder.length() > 1) {
+                                               builder.append("(&(")
+                                                               .append(LdifName.objectClass.name())
+                                                               .append("=")
+                                                               .append(LdifName.inetOrgPerson.name())
+                                                               .append(")(|");
+                                               builder.append(tmpBuilder.toString());
+                                               builder.append("))");
+                                       } else
+                                               builder.append("(").append(LdifName.objectClass.name())
+                                                               .append("=")
+                                                               .append(LdifName.inetOrgPerson.name())
+                                                               .append(")");
+                                       roles = userAdminWrapper.getUserAdmin().getRoles(
+                                                       builder.toString());
+                               } catch (InvalidSyntaxException e) {
+                                       throw new CmsException("Unable to get roles with filter: "
+                                                       + filter, e);
+                               }
+                               List<User> users = new ArrayList<User>();
+                               for (Role role : roles)
+                                       // Prevent current logged in user to perform batch on
+                                       // himself
+                                       if (!UserAdminUtils.isCurrentUser((User) role))
+                                               users.add((User) role);
+                               return users;
+                       }
+               }
+       }
+
+       /** Summary of input data before launching the process */
+       private class ValidateAndLaunchWizardPage extends WizardPage implements
+                       IPageChangedListener {
+               private static final long serialVersionUID = 7098918351451743853L;
+               private ChosenUsersTableViewer userTableCmp;
+
+               public ValidateAndLaunchWizardPage() {
+                       super("Validate and launch");
+                       setTitle("Validate and launch");
+               }
+
+               @Override
+               public void createControl(Composite parent) {
+                       Composite pageCmp = new Composite(parent, SWT.NO_FOCUS);
+                       pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+                       List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+                       columnDefs.add(new ColumnDefinition(new CommonNameLP(),
+                                       "Common Name", 150));
+                       columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
+                       columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain",
+                                       200));
+                       // Only show technical DN to admin
+                       if (UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN))
+                               columnDefs.add(new ColumnDefinition(new UserNameLP(),
+                                               "Distinguished Name", 300));
+                       userTableCmp = new ChosenUsersTableViewer(pageCmp, SWT.MULTI
+                                       | SWT.H_SCROLL | SWT.V_SCROLL);
+                       userTableCmp.setLayoutData(EclipseUiUtils.fillAll());
+                       userTableCmp.setColumnDefinitions(columnDefs);
+                       userTableCmp.populate(false, false);
+                       userTableCmp.refresh();
+                       setControl(pageCmp);
+                       // Add listener to update message when shown
+                       final IWizardContainer wContainer = this.getContainer();
+                       if (wContainer instanceof IPageChangeProvider) {
+                               ((IPageChangeProvider) wContainer).addPageChangedListener(this);
+                       }
+               }
+
+               @Override
+               public void pageChanged(PageChangedEvent event) {
+                       if (event.getSelectedPage() == this) {
+                               @SuppressWarnings({ "unchecked", "rawtypes" })
+                               Object[] values = ((ArrayList) userListPage.getSelectedUsers())
+                                               .toArray(new Object[userListPage.getSelectedUsers()
+                                                               .size()]);
+                               userTableCmp.getTableViewer().setInput(values);
+                               String msg = "Following batch action: ["
+                                               + chooseCommandPage.getCommandLbl()
+                                               + "] will be perfomed on the users listed below.\n";
+                               // + "Are you sure you want to proceed?";
+                               setMessage(msg);
+                       }
+               }
+
+               private class ChosenUsersTableViewer extends LdifUsersTable {
+                       private static final long serialVersionUID = 7814764735794270541L;
+
+                       public ChosenUsersTableViewer(Composite parent, int style) {
+                               super(parent, style);
+                       }
+
+                       @Override
+                       protected List<User> listFilteredElements(String filter) {
+                               return userListPage.getSelectedUsers();
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserEditor.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserEditor.java
new file mode 100644 (file)
index 0000000..6c0731d
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.parts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.argeo.osgi.useradmin.LdifName;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.argeo.security.ui.admin.internal.UiUserAdminListener;
+import org.argeo.security.ui.admin.internal.UserAdminWrapper;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.forms.AbstractFormPart;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Editor for a user, might be a user or a group. */
+public class UserEditor extends FormEditor {
+       private static final long serialVersionUID = 8357851520380820241L;
+
+       public final static String USER_EDITOR_ID = SecurityAdminPlugin.PLUGIN_ID
+                       + ".userEditor";
+       public final static String GROUP_EDITOR_ID = SecurityAdminPlugin.PLUGIN_ID
+                       + ".groupEditor";
+
+       /* DEPENDENCY INJECTION */
+       private UserAdminWrapper userAdminWrapper;
+       private UserAdmin userAdmin;
+
+       // Context
+       private User user;
+       private String username;
+
+       private NameChangeListener listener;
+
+       public void init(IEditorSite site, IEditorInput input)
+                       throws PartInitException {
+               super.init(site, input);
+               username = ((UserEditorInput) getEditorInput()).getUsername();
+               user = (User) userAdmin.getRole(username);
+
+               listener = new NameChangeListener(site.getShell().getDisplay(), user);
+               userAdminWrapper.addListener(listener);
+               updateEditorTitle(null);
+       }
+
+       /**
+        * returns the list of all authorization for the given user or of the
+        * current displayed user if parameter is null
+        */
+       protected List<User> getFlatGroups(User aUser) {
+               Authorization currAuth;
+               if (aUser == null)
+                       currAuth = userAdmin.getAuthorization(this.user);
+               else
+                       currAuth = userAdmin.getAuthorization(aUser);
+
+               String[] roles = currAuth.getRoles();
+
+               List<User> groups = new ArrayList<User>();
+               for (String roleStr : roles) {
+                       User currRole = (User) userAdmin.getRole(roleStr);
+                       if (!groups.contains(currRole))
+                               groups.add(currRole);
+               }
+               return groups;
+       }
+
+       /** Exposes the user (or group) that is displayed by the current editor */
+       protected User getDisplayedUser() {
+               return user;
+       }
+
+       void updateEditorTitle(String title) {
+               if (title == null) {
+                       String commonName = UserAdminUtils.getProperty(user,
+                                       LdifName.cn.name());
+                       title = "".equals(commonName) ? user.getName() : commonName;
+               }
+               setPartName(title);
+       }
+
+       protected void addPages() {
+               try {
+                       if (user.getType() == Role.GROUP)
+                               addPage(new GroupMainPage(this, userAdminWrapper));
+                       else
+                               addPage(new UserMainPage(this, userAdminWrapper));
+               } catch (Exception e) {
+                       throw new CmsException("Cannot add pages", e);
+               }
+       }
+
+       @Override
+       public void doSave(IProgressMonitor monitor) {
+               userAdminWrapper.beginTransactionIfNeeded();
+               commitPages(true);
+               userAdminWrapper.commitOrNotifyTransactionStateChange();
+               firePropertyChange(PROP_DIRTY);
+               userAdminWrapper.notifyListeners(new UserAdminEvent(null,
+                               UserAdminEvent.ROLE_REMOVED, user));
+       }
+
+       @Override
+       public void doSaveAs() {
+       }
+
+       @Override
+       public boolean isSaveAsAllowed() {
+               return false;
+       }
+
+       @Override
+       public void dispose() {
+               userAdminWrapper.removeListener(listener);
+               super.dispose();
+       }
+
+       // CONTROLERS FOR THIS EDITOR AND ITS PAGES
+
+       private class NameChangeListener extends UiUserAdminListener {
+
+               private final User user;
+
+               public NameChangeListener(Display display, User user) {
+                       super(display);
+                       this.user = user;
+               }
+
+               @Override
+               public void roleChangedToUiThread(UserAdminEvent event) {
+                       Role changedRole = event.getRole();
+                       if (changedRole == null || changedRole.equals(user))
+                               updateEditorTitle(null);
+               }
+       }
+
+       class MainInfoListener extends UiUserAdminListener {
+               private final AbstractFormPart part;
+
+               public MainInfoListener(Display display, AbstractFormPart part) {
+                       super(display);
+                       this.part = part;
+               }
+
+               @Override
+               public void roleChangedToUiThread(UserAdminEvent event) {
+                       // Rollback
+                       if (event.getRole() == null)
+                               part.markStale();
+               }
+       }
+
+       class GroupChangeListener extends UiUserAdminListener {
+               private final AbstractFormPart part;
+
+               public GroupChangeListener(Display display, AbstractFormPart part) {
+                       super(display);
+                       this.part = part;
+               }
+
+               @Override
+               public void roleChangedToUiThread(UserAdminEvent event) {
+                       // always mark as stale
+                       part.markStale();
+               }
+       }
+
+       /** Registers a listener that will notify this part */
+       class FormPartML implements ModifyListener {
+               private static final long serialVersionUID = 6299808129505381333L;
+               private AbstractFormPart formPart;
+
+               public FormPartML(AbstractFormPart generalPart) {
+                       this.formPart = generalPart;
+               }
+
+               public void modifyText(ModifyEvent e) {
+                       // Discard event when the control does not have the focus, typically
+                       // to avoid all editors being marked as dirty during a Rollback
+                       if (((Control) e.widget).isFocusControl())
+                               formPart.markDirty();
+               }
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+               this.userAdmin = userAdminWrapper.getUserAdmin();
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserEditorInput.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserEditorInput.java
new file mode 100644 (file)
index 0000000..fe129f8
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.parts;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IPersistableElement;
+
+/**
+ * Editor input for an user defined by unique name (usually a distinguished
+ * name).
+ */
+public class UserEditorInput implements IEditorInput {
+       private final String username;
+
+       public UserEditorInput(String username) {
+               this.username = username;
+       }
+
+       public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+               return null;
+       }
+
+       public boolean exists() {
+               return username != null;
+       }
+
+       public ImageDescriptor getImageDescriptor() {
+               return null;
+       }
+
+       public String getName() {
+               return username != null ? username : "<new user>";
+       }
+
+       public IPersistableElement getPersistable() {
+               return null;
+       }
+
+       public String getToolTipText() {
+               return username != null ? username : "<new user>";
+       }
+
+       public boolean equals(Object obj) {
+               if (!(obj instanceof UserEditorInput))
+                       return false;
+               if (((UserEditorInput) obj).getUsername() == null)
+                       return false;
+               return ((UserEditorInput) obj).getUsername().equals(username);
+       }
+
+       public String getUsername() {
+               return username;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java
new file mode 100644 (file)
index 0000000..db6eb53
--- /dev/null
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.parts;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.parts.LdifUsersTable;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.osgi.useradmin.LdifName;
+import org.argeo.security.ui.admin.SecurityAdminImages;
+import org.argeo.security.ui.admin.internal.UserAdminWrapper;
+import org.argeo.security.ui.admin.internal.parts.UserEditor.GroupChangeListener;
+import org.argeo.security.ui.admin.internal.parts.UserEditor.MainInfoListener;
+import org.argeo.security.ui.admin.internal.providers.CommonNameLP;
+import org.argeo.security.ui.admin.internal.providers.DomainNameLP;
+import org.argeo.security.ui.admin.internal.providers.RoleIconLP;
+import org.argeo.security.ui.admin.internal.providers.UserFilter;
+import org.argeo.security.ui.admin.internal.providers.UserNameLP;
+import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerDropAdapter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.dnd.TransferData;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.ui.forms.AbstractFormPart;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.SectionPart;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.FormPage;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.Section;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Display/edit the properties of a given user */
+public class UserMainPage extends FormPage implements ArgeoNames {
+       final static String ID = "UserEditor.mainPage";
+
+       private final UserEditor editor;
+       private UserAdminWrapper userAdminWrapper;
+
+       // Local configuration
+       private final int PRE_TITLE_INDENT = 10;
+
+       public UserMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) {
+               super(editor, ID, "Main");
+               this.editor = (UserEditor) editor;
+               this.userAdminWrapper = userAdminWrapper;
+       }
+
+       protected void createFormContent(final IManagedForm mf) {
+               ScrolledForm form = mf.getForm();
+               Composite body = form.getBody();
+               GridLayout mainLayout = new GridLayout();
+               // mainLayout.marginRight = 10;
+               body.setLayout(mainLayout);
+               User user = editor.getDisplayedUser();
+               appendOverviewPart(body, user);
+               // Remove to ability to force the password for his own user. The user
+               // must then use the change pwd feature
+               if (!UserAdminUtils.isCurrentUser(user))
+                       appendPasswordPart(body, user);
+               appendMemberOfPart(body, user);
+       }
+
+       /** Creates the general section */
+       private void appendOverviewPart(final Composite parent, final User user) {
+               FormToolkit tk = getManagedForm().getToolkit();
+
+               Section section = addSection(tk, parent, "Main information");
+               Composite body = (Composite) section.getClient();
+               body.setLayout(new GridLayout(2, false));
+
+               final Text distinguishedName = createLT(tk, body, "User Name",
+                               UserAdminUtils.getProperty(user, LdifName.uid.name()));
+               distinguishedName.setEnabled(false);
+
+               final Text commonName = createLT(tk, body, "Common Name",
+                               UserAdminUtils.getProperty(user, LdifName.cn.name()));
+               commonName.setEnabled(false);
+
+               final Text firstName = createLT(tk, body, "First name",
+                               UserAdminUtils.getProperty(user, LdifName.givenName.name()));
+
+               final Text lastName = createLT(tk, body, "Last name",
+                               UserAdminUtils.getProperty(user, LdifName.sn.name()));
+
+               final Text email = createLT(tk, body, "Email",
+                               UserAdminUtils.getProperty(user, LdifName.mail.name()));
+
+               // create form part (controller)
+               AbstractFormPart part = new SectionPart((Section) body.getParent()) {
+                       private MainInfoListener listener;
+
+                       @Override
+                       public void initialize(IManagedForm form) {
+                               super.initialize(form);
+                               listener = editor.new MainInfoListener(parent.getDisplay(),
+                                               this);
+                               userAdminWrapper.addListener(listener);
+                       }
+
+                       @Override
+                       public void dispose() {
+                               userAdminWrapper.removeListener(listener);
+                               super.dispose();
+                       }
+
+                       @SuppressWarnings("unchecked")
+                       public void commit(boolean onSave) {
+                               // TODO Sanity checks (mail validity...)
+                               user.getProperties().put(LdifName.givenName.name(),
+                                               firstName.getText());
+                               user.getProperties()
+                                               .put(LdifName.sn.name(), lastName.getText());
+                               user.getProperties().put(LdifName.cn.name(),
+                                               commonName.getText());
+                               user.getProperties().put(LdifName.mail.name(), email.getText());
+                               super.commit(onSave);
+                       }
+
+                       @Override
+                       public void refresh() {
+                               distinguishedName.setText(UserAdminUtils.getProperty(user,
+                                               LdifName.uid.name()));
+                               commonName.setText(UserAdminUtils.getProperty(user,
+                                               LdifName.cn.name()));
+                               firstName.setText(UserAdminUtils.getProperty(user,
+                                               LdifName.givenName.name()));
+                               lastName.setText(UserAdminUtils.getProperty(user,
+                                               LdifName.sn.name()));
+                               email.setText(UserAdminUtils.getProperty(user,
+                                               LdifName.mail.name()));
+                               refreshFormTitle(user);
+                               super.refresh();
+                       }
+               };
+
+               // Improve this: automatically generate CN when first or last name
+               // changes
+               ModifyListener cnML = new ModifyListener() {
+                       private static final long serialVersionUID = 4298649222869835486L;
+
+                       @Override
+                       public void modifyText(ModifyEvent event) {
+                               String first = firstName.getText();
+                               String last = lastName.getText();
+                               String cn = first.trim() + " " + last.trim() + " ";
+                               cn = cn.trim();
+                               commonName.setText(cn);
+                               getManagedForm().getForm().setText(cn);
+                               editor.updateEditorTitle(cn);
+                       }
+               };
+               firstName.addModifyListener(cnML);
+               lastName.addModifyListener(cnML);
+
+               ModifyListener defaultListener = editor.new FormPartML(part);
+               firstName.addModifyListener(defaultListener);
+               lastName.addModifyListener(defaultListener);
+               email.addModifyListener(defaultListener);
+               getManagedForm().addPart(part);
+       }
+
+       /** Creates the password section */
+       private void appendPasswordPart(Composite parent, final User user) {
+               FormToolkit tk = getManagedForm().getToolkit();
+               Section section = addSection(tk, parent, "Password");
+               Composite body = (Composite) section.getClient();
+               body.setLayout(new GridLayout(2, false));
+
+               // add widgets (view)
+               final Text password1 = createLP(tk, body, "New password", "");
+               final Text password2 = createLP(tk, body, "Repeat password", "");
+
+               // create form part (controller)
+               AbstractFormPart part = new SectionPart((Section) body.getParent()) {
+                       @SuppressWarnings("unchecked")
+                       public void commit(boolean onSave) {
+                               if (!password1.getText().equals("")
+                                               || !password2.getText().equals("")) {
+                                       if (password1.getText().equals(password2.getText())) {
+                                               char[] newPassword = password1.getText().toCharArray();
+                                               // userAdminWrapper.beginTransactionIfNeeded();
+                                               user.getCredentials().put(null, newPassword);
+                                               password1.setText("");
+                                               password2.setText("");
+                                               super.commit(onSave);
+                                       } else {
+                                               password1.setText("");
+                                               password2.setText("");
+                                               throw new CmsException("Passwords are not equals");
+                                       }
+                               }
+                       }
+               };
+               ModifyListener defaultListener = editor.new FormPartML(part);
+               password1.addModifyListener(defaultListener);
+               password2.addModifyListener(defaultListener);
+               getManagedForm().addPart(part);
+       }
+
+       private LdifUsersTable appendMemberOfPart(final Composite parent, User user) {
+               FormToolkit tk = getManagedForm().getToolkit();
+               Section section = addSection(tk, parent, "Roles");
+               Composite body = (Composite) section.getClient();
+               body.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+               boolean isAdmin = UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN);
+
+               // Displayed columns
+               List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+               columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24));
+               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name",
+                               150));
+               columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain Name",
+                               200));
+               // Only show technical DN to administrators
+               if (isAdmin)
+                       columnDefs.add(new ColumnDefinition(new UserNameLP(),
+                                       "Distinguished Name", 120));
+
+               // Create and configure the table
+               final LdifUsersTable userViewerCmp = new MyUserTableViewer(body,
+                               SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, user);
+
+               userViewerCmp.setColumnDefinitions(columnDefs);
+               if (isAdmin)
+                       userViewerCmp.populateWithStaticFilters(false, false);
+               else
+                       userViewerCmp.populate(true, false);
+               GridData gd = EclipseUiUtils.fillAll();
+               gd.heightHint = 300;
+               userViewerCmp.setLayoutData(gd);
+
+               // Controllers
+               TableViewer userViewer = userViewerCmp.getTableViewer();
+               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener());
+               int operations = DND.DROP_COPY | DND.DROP_MOVE;
+               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
+               GroupDropListener dropL = new GroupDropListener(userAdminWrapper,
+                               userViewer, user);
+               userViewer.addDropSupport(operations, tt, dropL);
+
+               SectionPart part = new SectionPart((Section) body.getParent()) {
+
+                       private GroupChangeListener listener;
+
+                       @Override
+                       public void initialize(IManagedForm form) {
+                               super.initialize(form);
+                               listener = editor.new GroupChangeListener(parent.getDisplay(),
+                                               this);
+                               userAdminWrapper.addListener(listener);
+                       }
+
+                       public void commit(boolean onSave) {
+                               super.commit(onSave);
+                       }
+
+                       @Override
+                       public void dispose() {
+                               userAdminWrapper.removeListener(listener);
+                               super.dispose();
+                       }
+
+                       @Override
+                       public void refresh() {
+                               userViewerCmp.refresh();
+                               super.refresh();
+                       }
+               };
+               getManagedForm().addPart(part);
+               addRemoveAbitily(part, userViewer, user);
+               return userViewerCmp;
+       }
+
+       private class MyUserTableViewer extends LdifUsersTable {
+               private static final long serialVersionUID = 2653790051461237329L;
+
+               private Button showSystemRoleBtn;
+
+               private final User user;
+               private final UserFilter userFilter;
+
+               public MyUserTableViewer(Composite parent, int style, User user) {
+                       super(parent, style, true);
+                       this.user = user;
+                       userFilter = new UserFilter();
+                       userFilter.setShowSystemRole(false);
+               }
+
+               protected void populateStaticFilters(Composite staticFilterCmp) {
+                       staticFilterCmp.setLayout(new GridLayout());
+                       showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK);
+                       showSystemRoleBtn.setText("Show system roles");
+                       showSystemRoleBtn.addSelectionListener(new SelectionAdapter() {
+                               private static final long serialVersionUID = -7033424592697691676L;
+
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       userFilter.setShowSystemRole(showSystemRoleBtn
+                                                       .getSelection());
+                                       refresh();
+                               }
+                       });
+               }
+
+               @Override
+               protected List<User> listFilteredElements(String filter) {
+                       List<User> users = (List<User>) editor.getFlatGroups(null);
+                       List<User> filteredUsers = new ArrayList<User>();
+                       if (users.contains(user))
+                               users.remove(user);
+                       userFilter.setSearchText(filter);
+                       for (User user : users)
+                               if (userFilter.select(null, null, user))
+                                       filteredUsers.add(user);
+                       return filteredUsers;
+               }
+       }
+
+       private void addRemoveAbitily(SectionPart sectionPart,
+                       TableViewer userViewer, User user) {
+               Section section = sectionPart.getSection();
+               ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
+               ToolBar toolbar = toolBarManager.createControl(section);
+               final Cursor handCursor = new Cursor(section.getDisplay(),
+                               SWT.CURSOR_HAND);
+               toolbar.setCursor(handCursor);
+               toolbar.addDisposeListener(new DisposeListener() {
+                       private static final long serialVersionUID = 3882131405820522925L;
+
+                       public void widgetDisposed(DisposeEvent e) {
+                               if ((handCursor != null) && (handCursor.isDisposed() == false)) {
+                                       handCursor.dispose();
+                               }
+                       }
+               });
+
+               String tooltip = "Remove " + UserAdminUtils.getUsername(user)
+                               + " from the below selected groups";
+               Action action = new RemoveMembershipAction(userViewer, user, tooltip,
+                               SecurityAdminImages.ICON_REMOVE_DESC);
+               toolBarManager.add(action);
+               toolBarManager.update(true);
+               section.setTextClient(toolbar);
+       }
+
+       private class RemoveMembershipAction extends Action {
+               private static final long serialVersionUID = -1337713097184522588L;
+
+               private final TableViewer userViewer;
+               private final User user;
+
+               RemoveMembershipAction(TableViewer userViewer, User user, String name,
+                               ImageDescriptor img) {
+                       super(name, img);
+                       this.userViewer = userViewer;
+                       this.user = user;
+               }
+
+               @Override
+               public void run() {
+                       ISelection selection = userViewer.getSelection();
+                       if (selection.isEmpty())
+                               return;
+
+                       @SuppressWarnings("unchecked")
+                       Iterator<Group> it = ((IStructuredSelection) selection).iterator();
+                       List<Group> groups = new ArrayList<Group>();
+                       while (it.hasNext()) {
+                               Group currGroup = it.next();
+                               groups.add(currGroup);
+                       }
+
+                       userAdminWrapper.beginTransactionIfNeeded();
+                       for (Group group : groups) {
+                               group.removeMember(user);
+                       }
+                       userAdminWrapper.commitOrNotifyTransactionStateChange();
+                       for (Group group : groups) {
+                               userAdminWrapper.notifyListeners(new UserAdminEvent(null,
+                                               UserAdminEvent.ROLE_CHANGED, group));
+                       }
+               }
+       }
+
+       /**
+        * Defines the table as being a potential target to add group memberships
+        * (roles) to this user
+        */
+       private class GroupDropListener extends ViewerDropAdapter {
+               private static final long serialVersionUID = 2893468717831451621L;
+
+               private final UserAdminWrapper myUserAdminWrapper;
+               private final User myUser;
+
+               public GroupDropListener(UserAdminWrapper userAdminWrapper,
+                               Viewer userViewer, User user) {
+                       super(userViewer);
+                       this.myUserAdminWrapper = userAdminWrapper;
+                       this.myUser = user;
+               }
+
+               @Override
+               public boolean validateDrop(Object target, int operation,
+                               TransferData transferType) {
+                       // Target is always OK in a list only view
+                       // TODO check if not a string
+                       boolean validDrop = true;
+                       return validDrop;
+               }
+
+               @Override
+               public void drop(DropTargetEvent event) {
+                       String name = (String) event.data;
+                       UserAdmin myUserAdmin = myUserAdminWrapper.getUserAdmin();
+                       Role role = myUserAdmin.getRole(name);
+                       // TODO this check should be done before.
+                       if (role.getType() == Role.GROUP) {
+                               // TODO check if the user is already member of this group
+
+                               myUserAdminWrapper.beginTransactionIfNeeded();
+                               Group group = (Group) role;
+                               group.addMember(myUser);
+                               userAdminWrapper.commitOrNotifyTransactionStateChange();
+                               myUserAdminWrapper.notifyListeners(new UserAdminEvent(null,
+                                               UserAdminEvent.ROLE_CHANGED, group));
+                       }
+                       super.drop(event);
+               }
+
+               @Override
+               public boolean performDrop(Object data) {
+                       // userTableViewerCmp.refresh();
+                       return true;
+               }
+       }
+
+       // LOCAL HELPERS
+       private void refreshFormTitle(User group) {
+               getManagedForm().getForm().setText(
+                               UserAdminUtils.getProperty(group, LdifName.cn.name()));
+       }
+
+       /** Appends a section with a title */
+       private Section addSection(FormToolkit tk, Composite parent, String title) {
+               Section section = tk.createSection(parent, Section.TITLE_BAR);
+               GridData gd = EclipseUiUtils.fillWidth();
+               gd.verticalAlignment = PRE_TITLE_INDENT;
+               section.setLayoutData(gd);
+               section.setText(title);
+               // section.getMenu().setVisible(true);
+
+               Composite body = tk.createComposite(section, SWT.WRAP);
+               body.setLayoutData(EclipseUiUtils.fillAll());
+               section.setClient(body);
+
+               return section;
+       }
+
+       /** Creates label and multiline text. */
+       Text createLMT(FormToolkit toolkit, Composite body, String label,
+                       String value) {
+               Label lbl = toolkit.createLabel(body, label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Text text = toolkit.createText(body, value, SWT.BORDER | SWT.MULTI);
+               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
+               return text;
+       }
+
+       /** Creates label and password. */
+       Text createLP(FormToolkit toolkit, Composite body, String label,
+                       String value) {
+               Label lbl = toolkit.createLabel(body, label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Text text = toolkit.createText(body, value, SWT.BORDER | SWT.PASSWORD);
+               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+               return text;
+       }
+
+       /** Creates label and text. */
+       Text createLT(FormToolkit toolkit, Composite body, String label,
+                       String value) {
+               Label lbl = toolkit.createLabel(body, label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Text text = toolkit.createText(body, value, SWT.BORDER);
+               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+               return text;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UsersView.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/parts/UsersView.java
new file mode 100644 (file)
index 0000000..a397432
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.ui.admin.internal.parts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.parts.LdifUsersTable;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.osgi.useradmin.LdifName;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.argeo.security.ui.admin.internal.UiUserAdminListener;
+import org.argeo.security.ui.admin.internal.UserAdminWrapper;
+import org.argeo.security.ui.admin.internal.providers.CommonNameLP;
+import org.argeo.security.ui.admin.internal.providers.DomainNameLP;
+import org.argeo.security.ui.admin.internal.providers.MailLP;
+import org.argeo.security.ui.admin.internal.providers.UserDragListener;
+import org.argeo.security.ui.admin.internal.providers.UserNameLP;
+import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.part.ViewPart;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdminEvent;
+import org.osgi.service.useradmin.UserAdminListener;
+
+/** List all users with filter - based on Ldif userAdmin */
+public class UsersView extends ViewPart implements ArgeoNames {
+       // private final static Log log = LogFactory.getLog(UsersView.class);
+
+       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
+                       + ".usersView";
+
+       /* DEPENDENCY INJECTION */
+       private UserAdminWrapper userAdminWrapper;
+
+       // UI Objects
+       private LdifUsersTable userTableViewerCmp;
+       private TableViewer userViewer;
+       private List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+
+       private UserAdminListener listener;
+
+       @Override
+       public void createPartControl(Composite parent) {
+
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+               // Define the displayed columns
+               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name",
+                               150));
+               columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
+               columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
+               // Only show technical DN to admin
+               if (UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN))
+                       columnDefs.add(new ColumnDefinition(new UserNameLP(),
+                                       "Distinguished Name", 300));
+
+               // Create and configure the table
+               userTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI
+                               | SWT.H_SCROLL | SWT.V_SCROLL);
+               userTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll());
+               userTableViewerCmp.setColumnDefinitions(columnDefs);
+               userTableViewerCmp.populate(true, false);
+
+               // Links
+               userViewer = userTableViewerCmp.getTableViewer();
+               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener());
+               getViewSite().setSelectionProvider(userViewer);
+
+               // Really?
+               userTableViewerCmp.refresh();
+
+               // Drag and drop
+               int operations = DND.DROP_COPY | DND.DROP_MOVE;
+               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
+               userViewer.addDragSupport(operations, tt, new UserDragListener(
+                               userViewer));
+
+               // Register a useradmin listener
+               listener = new MyUiUAListener(parent.getDisplay());
+               userAdminWrapper.addListener(listener);
+       }
+
+       private class MyUiUAListener extends UiUserAdminListener {
+               public MyUiUAListener(Display display) {
+                       super(display);
+               }
+
+               @Override
+               public void roleChangedToUiThread(UserAdminEvent event) {
+                       if (userViewer != null && !userViewer.getTable().isDisposed())
+                               refresh();
+               }
+       }
+
+       private class MyUserTableViewer extends LdifUsersTable {
+               private static final long serialVersionUID = 8467999509931900367L;
+
+               private final String[] knownProps = { LdifName.uid.name(),
+                               LdifName.dn.name(), LdifName.cn.name(),
+                               LdifName.givenName.name(), LdifName.sn.name(),
+                               LdifName.mail.name() };
+
+               public MyUserTableViewer(Composite parent, int style) {
+                       super(parent, style);
+               }
+
+               @Override
+               protected List<User> listFilteredElements(String filter) {
+                       Role[] roles;
+
+                       try {
+                               StringBuilder builder = new StringBuilder();
+
+                               StringBuilder tmpBuilder = new StringBuilder();
+                               if (EclipseUiUtils.notEmpty(filter))
+                                       for (String prop : knownProps) {
+                                               tmpBuilder.append("(");
+                                               tmpBuilder.append(prop);
+                                               tmpBuilder.append("=*");
+                                               tmpBuilder.append(filter);
+                                               tmpBuilder.append("*)");
+                                       }
+                               if (tmpBuilder.length() > 1) {
+                                       builder.append("(&(").append(LdifName.objectClass.name())
+                                                       .append("=").append(LdifName.inetOrgPerson.name())
+                                                       .append(")(|");
+                                       builder.append(tmpBuilder.toString());
+                                       builder.append("))");
+                               } else
+                                       builder.append("(").append(LdifName.objectClass.name())
+                                                       .append("=").append(LdifName.inetOrgPerson.name())
+                                                       .append(")");
+                               roles = userAdminWrapper.getUserAdmin().getRoles(
+                                               builder.toString());
+                       } catch (InvalidSyntaxException e) {
+                               throw new CmsException("Unable to get roles with filter: "
+                                               + filter, e);
+                       }
+                       List<User> users = new ArrayList<User>();
+                       for (Role role : roles)
+                               // if (role.getType() == Role.USER && role.getType() !=
+                               // Role.GROUP)
+                               users.add((User) role);
+                       return users;
+               }
+       }
+
+       public void refresh() {
+               userTableViewerCmp.refresh();
+       }
+
+       // Override generic view methods
+       @Override
+       public void dispose() {
+               userAdminWrapper.removeListener(listener);
+               super.dispose();
+       }
+
+       @Override
+       public void setFocus() {
+               userTableViewerCmp.setFocus();
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java
new file mode 100644 (file)
index 0000000..d45c0b6
--- /dev/null
@@ -0,0 +1,15 @@
+package org.argeo.security.ui.admin.internal.providers;
+
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.argeo.osgi.useradmin.LdifName;
+import org.osgi.service.useradmin.User;
+
+/** Simply declare a label provider that returns the common name of a user */
+public class CommonNameLP extends UserAdminAbstractLP {
+       private static final long serialVersionUID = 5256703081044911941L;
+
+       @Override
+       public String getText(User user) {
+               return UserAdminUtils.getProperty(user, LdifName.cn.name());
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/DomainNameLP.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/DomainNameLP.java
new file mode 100644 (file)
index 0000000..795fd0a
--- /dev/null
@@ -0,0 +1,14 @@
+package org.argeo.security.ui.admin.internal.providers;
+
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.osgi.service.useradmin.User;
+
+/** The human friendly domain name for the corresponding user. */
+public class DomainNameLP extends UserAdminAbstractLP {
+       private static final long serialVersionUID = 5256703081044911941L;
+
+       @Override
+       public String getText(User user) {
+               return UserAdminUtils.getDomainName(user);
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/MailLP.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/MailLP.java
new file mode 100644 (file)
index 0000000..0a6dcb6
--- /dev/null
@@ -0,0 +1,15 @@
+package org.argeo.security.ui.admin.internal.providers;
+
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.argeo.osgi.useradmin.LdifName;
+import org.osgi.service.useradmin.User;
+
+/** Simply declare a label provider that returns the Primary Mail of a user */
+public class MailLP extends UserAdminAbstractLP {
+       private static final long serialVersionUID = 8329764452141982707L;
+
+       @Override
+       public String getText(User user) {
+               return UserAdminUtils.getProperty(user, LdifName.mail.name());
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/RoleIconLP.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/RoleIconLP.java
new file mode 100644 (file)
index 0000000..bb19220
--- /dev/null
@@ -0,0 +1,29 @@
+package org.argeo.security.ui.admin.internal.providers;
+
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.security.ui.admin.SecurityAdminImages;
+import org.eclipse.swt.graphics.Image;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+/** Provide a bundle specific image depending on the current user type */
+public class RoleIconLP extends UserAdminAbstractLP {
+       private static final long serialVersionUID = 6550449442061090388L;
+
+       @Override
+       public String getText(User user) {
+               return "";
+       }
+
+       @Override
+       public Image getImage(Object element) {
+               User user = (User) element;
+               String dn = user.getName();
+               if (dn.endsWith(AuthConstants.ROLES_BASEDN))
+                       return SecurityAdminImages.ICON_ROLE;
+               else if (user.getType() == Role.GROUP)
+                       return SecurityAdminImages.ICON_GROUP;
+               else
+                       return SecurityAdminImages.ICON_USER;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserAdminAbstractLP.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserAdminAbstractLP.java
new file mode 100644 (file)
index 0000000..2b0a13d
--- /dev/null
@@ -0,0 +1,66 @@
+package org.argeo.security.ui.admin.internal.providers;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Display;
+import org.osgi.service.useradmin.User;
+
+/**
+ * Utility class that add font modifications to a column label provider
+ * depending on the given user properties
+ */
+public abstract class UserAdminAbstractLP extends ColumnLabelProvider {
+       private static final long serialVersionUID = 137336765024922368L;
+
+       // private Font italic;
+       private Font bold;
+
+       @Override
+       public Font getFont(Object element) {
+               // Self as bold
+               try {
+                       LdapName selfUserName = UserAdminUtils.getCurrentUserLdapName();
+                       String userName = ((User) element).getName();
+                       LdapName userLdapName = new LdapName(userName);
+                       if (userLdapName.equals(selfUserName)) {
+                               if (bold == null)
+                                       bold = JFaceResources.getFontRegistry()
+                                                       .defaultFontDescriptor().setStyle(SWT.BOLD)
+                                                       .createFont(Display.getCurrent());
+                               return bold;
+                       }
+               } catch (InvalidNameException e) {
+                       throw new CmsException("cannot parse dn for " + element, e);
+               }
+
+               // Disabled as Italic
+               // Node userProfile = (Node) elem;
+               // if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean())
+               // return italic;
+
+               return null;
+               // return super.getFont(element);
+       }
+
+       @Override
+       public String getText(Object element) {
+               User user = (User) element;
+               return getText(user);
+       }
+
+       public void setDisplay(Display display) {
+               // italic = JFaceResources.getFontRegistry().defaultFontDescriptor()
+               // .setStyle(SWT.ITALIC).createFont(display);
+               bold = JFaceResources.getFontRegistry().defaultFontDescriptor()
+                               .setStyle(SWT.BOLD).createFont(Display.getCurrent());
+       }
+
+       public abstract String getText(User user);
+}
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserDragListener.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserDragListener.java
new file mode 100644 (file)
index 0000000..f60b64c
--- /dev/null
@@ -0,0 +1,40 @@
+package org.argeo.security.ui.admin.internal.providers;
+
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.swt.dnd.DragSourceListener;
+import org.osgi.service.useradmin.User;
+
+/** Default drag listener to modify group and users via the UI */
+public class UserDragListener implements DragSourceListener {
+       private static final long serialVersionUID = -2074337775033781454L;
+       private final Viewer viewer;
+
+       public UserDragListener(Viewer viewer) {
+               this.viewer = viewer;
+       }
+
+       public void dragStart(DragSourceEvent event) {
+               // TODO implement finer checks
+               IStructuredSelection selection = (IStructuredSelection) viewer
+                               .getSelection();
+               if (selection.isEmpty() || selection.size() > 1)
+                       event.doit = false;
+               else
+                       event.doit = true;
+       }
+
+       public void dragSetData(DragSourceEvent event) {
+               // TODO Support multiple selection
+               Object obj = ((IStructuredSelection) viewer.getSelection())
+                               .getFirstElement();
+               if (obj != null) {
+                       User user = (User) obj;
+                       event.data = user.getName();
+               }
+       }
+
+       public void dragFinished(DragSourceEvent event) {
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java
new file mode 100644 (file)
index 0000000..5f753d1
--- /dev/null
@@ -0,0 +1,60 @@
+package org.argeo.security.ui.admin.internal.providers;
+
+import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty;
+
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.cms.util.useradmin.UserAdminUtils;
+import org.argeo.osgi.useradmin.LdifName;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.osgi.service.useradmin.User;
+
+public class UserFilter extends ViewerFilter {
+       private static final long serialVersionUID = 5082509381672880568L;
+
+       private String searchString;
+       private boolean showSystemRole = true;
+
+       private final String[] knownProps = { LdifName.dn.name(),
+                       LdifName.cn.name(), LdifName.givenName.name(), LdifName.sn.name(),
+                       LdifName.uid.name(), LdifName.description.name(),
+                       LdifName.mail.name() };
+
+       public void setSearchText(String s) {
+               // ensure that the value can be used for matching
+               if (notEmpty(s))
+                       searchString = ".*" + s.toLowerCase() + ".*";
+               else
+                       searchString = ".*";
+       }
+
+       public void setShowSystemRole(boolean showSystemRole) {
+               this.showSystemRole = showSystemRole;
+       }
+
+       @Override
+       public boolean select(Viewer viewer, Object parentElement, Object element) {
+               User user = (User) element;
+               if (!showSystemRole
+                               && user.getName().matches(
+                                               ".*(" + AuthConstants.ROLES_BASEDN + ")"))
+                       // UserAdminUtils.getProperty(user, LdifName.dn.name())
+                       // .toLowerCase().endsWith(AuthConstants.ROLES_BASEDN))
+                       return false;
+
+               if (searchString == null || searchString.length() == 0)
+                       return true;
+
+               if (user.getName().matches(searchString))
+                       return true;
+
+               for (String key : knownProps) {
+                       String currVal = UserAdminUtils.getProperty(user, key);
+                       if (notEmpty(currVal)
+                                       && currVal.toLowerCase().matches(searchString))
+                               return true;
+               }
+               return false;
+       }
+
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserNameLP.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserNameLP.java
new file mode 100644 (file)
index 0000000..a2a15c1
--- /dev/null
@@ -0,0 +1,13 @@
+package org.argeo.security.ui.admin.internal.providers;
+
+import org.osgi.service.useradmin.User;
+
+/** Simply declare a label provider that returns the username of a user */
+public class UserNameLP extends UserAdminAbstractLP {
+       private static final long serialVersionUID = 6550449442061090388L;
+
+       @Override
+       public String getText(User user) {
+               return user.getName();
+       }
+}
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserTableDefaultDClickListener.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserTableDefaultDClickListener.java
new file mode 100644 (file)
index 0000000..8f4a35a
--- /dev/null
@@ -0,0 +1,44 @@
+package org.argeo.security.ui.admin.internal.providers;
+
+import org.argeo.cms.CmsException;
+import org.argeo.eclipse.ui.workbench.WorkbenchUiPlugin;
+import org.argeo.security.ui.admin.internal.parts.UserEditor;
+import org.argeo.security.ui.admin.internal.parts.UserEditorInput;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.User;
+
+/**
+ * Default double click listener for the various user tables, will open the
+ * clicked item in the editor
+ */
+public class UserTableDefaultDClickListener implements IDoubleClickListener {
+       public void doubleClick(DoubleClickEvent evt) {
+               if (evt.getSelection().isEmpty())
+                       return;
+               Object obj = ((IStructuredSelection) evt.getSelection())
+                               .getFirstElement();
+               User user = (User) obj;
+               IWorkbenchWindow iww = WorkbenchUiPlugin.getDefault().getWorkbench()
+                               .getActiveWorkbenchWindow();
+               IWorkbenchPage iwp = iww.getActivePage();
+               UserEditorInput uei = new UserEditorInput(user.getName());
+
+               try {
+                       // Works around the fact that dynamic setting of the editor icon
+                       // causes NPE after a login/logout on RAP
+                       if (user instanceof Group)
+                               iwp.openEditor(uei, UserEditor.GROUP_EDITOR_ID);
+                       else
+                               iwp.openEditor(uei, UserEditor.USER_EDITOR_ID);
+               } catch (PartInitException pie) {
+                       throw new CmsException("Unable to open UserEditor for " + user,
+                                       pie);
+               }
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserTransactionProvider.java b/org.argeo.cms.ui.workbench/src/org/argeo/security/ui/admin/internal/providers/UserTransactionProvider.java
new file mode 100644 (file)
index 0000000..4ba304b
--- /dev/null
@@ -0,0 +1,74 @@
+package org.argeo.security.ui.admin.internal.providers;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.transaction.Status;
+import javax.transaction.UserTransaction;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.eclipse.ui.AbstractSourceProvider;
+import org.eclipse.ui.ISources;
+
+/** Observe and notify UI on UserTransaction state changes */
+public class UserTransactionProvider extends AbstractSourceProvider {
+       private final static Log log = LogFactory
+                       .getLog(UserTransactionProvider.class);
+
+       public final static String TRANSACTION_STATE = SecurityAdminPlugin.PLUGIN_ID
+                       + ".userTransactionState";
+       public final static String STATUS_ACTIVE = "status.active";
+       public final static String STATUS_NO_TRANSACTION = "status.noTransaction";
+
+       /* DEPENDENCY INJECTION */
+       private UserTransaction userTransaction;
+
+       @Override
+       public String[] getProvidedSourceNames() {
+               return new String[] { TRANSACTION_STATE};
+       }
+
+       @Override
+       public Map<String, String> getCurrentState() {
+               Map<String, String> currentState = new HashMap<String, String>(1);
+                       currentState.put(TRANSACTION_STATE, getInternalCurrentState());
+               return currentState;
+       }
+
+       @Override
+       public void dispose() {
+       }
+
+       private String getInternalCurrentState() {
+               try {
+                       String transactionState;
+                       if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION)
+                               transactionState = STATUS_NO_TRANSACTION;
+                       else
+                               // if (userTransaction.getStatus() == Status.STATUS_ACTIVE)
+                               transactionState = STATUS_ACTIVE;
+                       return transactionState;
+               } catch (Exception e) {
+                       throw new CmsException("Unable to begin transaction", e);
+               }
+       }
+
+       /** Publishes the ability to notify a state change */
+       public void fireTransactionStateChange() {
+               try {
+                       fireSourceChanged(ISources.WORKBENCH, TRANSACTION_STATE,
+                                       getInternalCurrentState());
+               } catch (Exception e) {
+                       log.warn("Cannot fire transaction state change event. Caught exception: "
+                                       + e.getClass().getCanonicalName() + " - " + e.getMessage());
+               }
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserTransaction(UserTransaction userTransaction) {
+               this.userTransaction = userTransaction;
+       }
+}
\ No newline at end of file
index 2a27b633415657b9621eaf1baa0c4859bb3f0ae3..dbbe1590d78a3565dbf13edaf408141e449eeb7f 100644 (file)
@@ -8,22 +8,22 @@
                <relativePath>..</relativePath>
        </parent>
        <artifactId>org.argeo.cms</artifactId>
-       <name>Commons CMS</name>
+       <name>Argeo CMS</name>
        <packaging>jar</packaging>
        <dependencies>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.cms.api</artifactId>
+                       <artifactId>org.argeo.node.api</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.server.jcr</artifactId>
+                       <artifactId>org.argeo.jcr</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.security.core</artifactId>
+                       <artifactId>org.argeo.enterprise</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
                <dependency>
diff --git a/org.argeo.eclipse.ui.workbench.rap/.project b/org.argeo.eclipse.ui.workbench.rap/.project
deleted file mode 100644 (file)
index 751e9d7..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.eclipse.ui.workbench.rap</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments />
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments />
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.pde.PluginNature</nature>
-       </natures>
-</projectDescription>
diff --git a/org.argeo.eclipse.ui.workbench.rap/META-INF/spring/osgi.xml b/org.argeo.eclipse.ui.workbench.rap/META-INF/spring/osgi.xml
deleted file mode 100644 (file)
index 206a72a..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>\r
-<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
-       xmlns:util="http://www.springframework.org/schema/util"\r
-       xsi:schemaLocation="http://www.springframework.org/schema/osgi  \r
-       http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
-       http://www.springframework.org/schema/beans   \r
-       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\r
-       http://www.springframework.org/schema/util\r
-       http://www.springframework.org/schema/util/spring-util-2.5.xsd">\r
-\r
-       <service interface="org.eclipse.rap.rwt.application.ApplicationConfiguration">\r
-               <service-properties>\r
-                       <beans:entry key="contextName" value="ui" />\r
-               </service-properties>\r
-               <beans:bean\r
-                       class="org.eclipse.rap.ui.internal.servlet.WorkbenchApplicationConfiguration" />\r
-       </service>\r
-</beans:beans>
\ No newline at end of file
diff --git a/org.argeo.eclipse.ui.workbench.rap/bnd.bnd b/org.argeo.eclipse.ui.workbench.rap/bnd.bnd
deleted file mode 100644 (file)
index 1b2939e..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-Bundle-SymbolicName: org.argeo.eclipse.ui.workbench.rap;singleton:=true
-Bundle-ActivationPolicy: lazy
-
-Fragment-Host: org.eclipse.rap.ui.workbench
diff --git a/org.argeo.eclipse.ui.workbench.rap/build.properties b/org.argeo.eclipse.ui.workbench.rap/build.properties
deleted file mode 100644 (file)
index 485b266..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-source.. = src/,\
-           ext/test/
diff --git a/org.argeo.eclipse.ui.workbench.rap/pom.xml b/org.argeo.eclipse.ui.workbench.rap/pom.xml
deleted file mode 100644 (file)
index baf0d30..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-       <modelVersion>4.0.0</modelVersion>
-       <parent>
-               <groupId>org.argeo.commons</groupId>
-               <version>2.1.46-SNAPSHOT</version>
-               <artifactId>argeo-commons</artifactId>
-               <relativePath>..</relativePath>
-       </parent>
-       <artifactId>org.argeo.eclipse.ui.workbench.rap</artifactId>
-       <name>Commons Eclipse Workbench RAP</name>
-</project>
index e12e5170d0cfd464861f1b3145d11c55f57458d4..612897f39d6b0345f05512b6a103f0cb54b03fb5 100644 (file)
@@ -1,4 +1,5 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
                <groupId>org.argeo.commons</groupId>
                        <version>2.1.46-SNAPSHOT</version>
                        <scope>provided</scope>
                </dependency>
+               <!-- Do we really need to have a dependency -->
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.enterprise</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
        </dependencies>
 </project>
\ No newline at end of file
index 9c18135132d0af9ee145df0959c6b42a82311c1f..d5f26c54b507774f20bc7940271c6677f34ac483 100644 (file)
                </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.server.jcr</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.security.core</artifactId>
+                       <artifactId>org.argeo.jcr</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
        </dependencies>
diff --git a/org.argeo.enterprise/.classpath b/org.argeo.enterprise/.classpath
new file mode 100644 (file)
index 0000000..4e5da1d
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src" />
+       <classpathentry kind="src" path="ext/test" />
+       <classpathentry kind="con"
+               path="org.eclipse.pde.core.requiredPlugins" />
+       <classpathentry kind="con"
+               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" />
+       <classpathentry kind="output" path="bin" />
+</classpath>
diff --git a/org.argeo.enterprise/.project b/org.argeo.enterprise/.project
new file mode 100644 (file)
index 0000000..5de2f0a
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.enterprise</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.eclipse.pde.PluginNature</nature>
+       </natures>
+</projectDescription>
diff --git a/org.argeo.enterprise/bnd.bnd b/org.argeo.enterprise/bnd.bnd
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/org.argeo.enterprise/build.properties b/org.argeo.enterprise/build.properties
new file mode 100644 (file)
index 0000000..af03ba4
--- /dev/null
@@ -0,0 +1,8 @@
+source.. = src/,\
+           ext/test/
+additional.bundles = org.junit,\
+                     org.slf4j.commons.logging,\
+                     org.slf4j.api,\
+                     org.slf4j.log4j12,\
+                     org.apache.log4j,\
+                     bitronix.tm
diff --git a/org.argeo.enterprise/ext/test/log4j.properties b/org.argeo.enterprise/ext/test/log4j.properties
new file mode 100644 (file)
index 0000000..ef73566
--- /dev/null
@@ -0,0 +1,28 @@
+log4j.rootLogger=WARN, console
+
+## Levels
+log4j.logger.org.argeo=TRACE
+
+log4j.logger.org.hibernate=WARN
+
+log4j.logger.org.springframework=WARN
+#log4j.logger.org.springframework.web=DEBUG
+#log4j.logger.org.springframework.jms=WARN
+#log4j.logger.org.springframework.security=WARN
+
+log4j.logger.org.apache.activemq=WARN
+log4j.logger.org.apache.activemq.transport=WARN
+log4j.logger.org.apache.activemq.ActiveMQMessageConsumer=INFO
+log4j.logger.org.apache.activemq.ActiveMQMessageProducer=INFO
+
+log4j.logger.org.apache.catalina=INFO
+log4j.logger.org.apache.coyote=INFO
+log4j.logger.org.apache.tomcat=INFO
+
+## Appenders
+# console is set to be a ConsoleAppender.
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+
+# console uses PatternLayout.
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n
diff --git a/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/BasicTestConstants.java b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/BasicTestConstants.java
new file mode 100644 (file)
index 0000000..98b8bc9
--- /dev/null
@@ -0,0 +1,9 @@
+package org.argeo.osgi.useradmin;
+
+interface BasicTestConstants {
+       String BASE_DN = "dc=example,dc=com";
+       String ROOT_USER_DN = "uid=root,ou=users," + BASE_DN;
+       String DEMO_USER_DN = "uid=demo,ou=users," + BASE_DN;
+       String ADMIN_GROUP_DN = "cn=admin,ou=groups," + BASE_DN;
+       String EDITORS_GROUP_DN = "cn=editors,ou=groups," + BASE_DN;
+}
diff --git a/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java
new file mode 100644 (file)
index 0000000..fecf5dd
--- /dev/null
@@ -0,0 +1,49 @@
+package org.argeo.osgi.useradmin;
+
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+import java.util.SortedMap;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapName;
+
+import org.argeo.util.naming.LdifParser;
+
+import junit.framework.TestCase;
+
+public class LdifParserTest extends TestCase implements BasicTestConstants {
+       public void testBasicLdif() throws Exception {
+               LdifParser ldifParser = new LdifParser();
+               SortedMap<LdapName, Attributes> res = ldifParser.read(getClass()
+                               .getResourceAsStream("basic.ldif"));
+               LdapName rootDn = new LdapName(ROOT_USER_DN);
+               Attributes rootAttributes = res.get(rootDn);
+               assertNotNull(rootAttributes);
+               assertEquals("Superuser",
+                               rootAttributes.get(LdifName.description.name()).get());
+               byte[] rawPwEntry = (byte[]) rootAttributes.get(
+                               LdifName.userPassword.name()).get();
+               assertEquals("{SHA}ieSV55Qc+eQOaYDRSha/AjzNTJE=",
+                               new String(rawPwEntry));
+               byte[] hashedPassword = DigestUtils.sha1("demo".getBytes());
+               assertEquals("{SHA}" + Base64.getEncoder().encodeToString(hashedPassword),
+                               new String(rawPwEntry));
+
+               LdapName adminDn = new LdapName(ADMIN_GROUP_DN);
+               Attributes adminAttributes = res.get(adminDn);
+               assertNotNull(adminAttributes);
+               Attribute memberAttribute = adminAttributes.get(LdifName.member.name());
+               assertNotNull(memberAttribute);
+               NamingEnumeration<?> members = memberAttribute.getAll();
+               List<String> users = new ArrayList<String>();
+               while (members.hasMore()) {
+                       Object value = members.next();
+                       users.add(value.toString());
+               }
+               assertEquals(1, users.size());
+               assertEquals(rootDn, new LdapName(users.get(0)));
+       }
+}
diff --git a/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java
new file mode 100644 (file)
index 0000000..a8a7d22
--- /dev/null
@@ -0,0 +1,191 @@
+package org.argeo.osgi.useradmin;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.UUID;
+
+import javax.transaction.TransactionManager;
+
+import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+import bitronix.tm.BitronixTransactionManager;
+import bitronix.tm.TransactionManagerServices;
+import bitronix.tm.resource.ehcache.EhCacheXAResourceProducer;
+import junit.framework.TestCase;
+
+public class LdifUserAdminTest extends TestCase implements BasicTestConstants {
+       private BitronixTransactionManager tm;
+       private URI uri;
+       private AbstractUserDirectory userAdmin;
+
+       public void testConcurrent() throws Exception {
+       }
+
+       @SuppressWarnings("unchecked")
+       public void testEdition() throws Exception {
+               User demoUser = (User) userAdmin.getRole(DEMO_USER_DN);
+               assertNotNull(demoUser);
+
+               tm.begin();
+               String newName = "demo";
+               demoUser.getProperties().put("cn", newName);
+               assertEquals(newName, demoUser.getProperties().get("cn"));
+               tm.commit();
+               persistAndRestart();
+               assertEquals(newName, demoUser.getProperties().get("cn"));
+
+               tm.begin();
+               userAdmin.removeRole(DEMO_USER_DN);
+               tm.commit();
+               persistAndRestart();
+
+               // check data
+               Role[] search = userAdmin.getRoles("(objectclass=inetOrgPerson)");
+               assertEquals(1, search.length);
+               Group editorGroup = (Group) userAdmin.getRole(EDITORS_GROUP_DN);
+               assertNotNull(editorGroup);
+               Role[] members = editorGroup.getMembers();
+               assertEquals(1, members.length);
+       }
+
+       public void testRetrieve() throws Exception {
+               // users
+               User rootUser = (User) userAdmin.getRole(ROOT_USER_DN);
+               assertNotNull(rootUser);
+               User demoUser = (User) userAdmin.getRole(DEMO_USER_DN);
+               assertNotNull(demoUser);
+
+               // groups
+               Group adminGroup = (Group) userAdmin.getRole(ADMIN_GROUP_DN);
+               assertNotNull(adminGroup);
+               Role[] members = adminGroup.getMembers();
+               assertEquals(1, members.length);
+               assertEquals(rootUser, members[0]);
+
+               Group editorGroup = (Group) userAdmin.getRole(EDITORS_GROUP_DN);
+               assertNotNull(editorGroup);
+               members = editorGroup.getMembers();
+               assertEquals(2, members.length);
+               assertEquals(adminGroup, members[0]);
+               assertEquals(demoUser, members[1]);
+
+               Authorization rootAuth = userAdmin.getAuthorization(rootUser);
+               List<String> rootRoles = Arrays.asList(rootAuth.getRoles());
+               assertEquals(3, rootRoles.size());
+               assertTrue(rootRoles.contains(ROOT_USER_DN));
+               assertTrue(rootRoles.contains(ADMIN_GROUP_DN));
+               assertTrue(rootRoles.contains(EDITORS_GROUP_DN));
+
+               // properties
+               assertEquals("root@localhost", rootUser.getProperties().get("mail"));
+
+               // credentials
+               byte[] hashedPassword = ("{SHA}" + Base64.getEncoder().encodeToString(DigestUtils.sha1("demo".getBytes())))
+                               .getBytes();
+               assertTrue(rootUser.hasCredential(LdifName.userPassword.name(), hashedPassword));
+               assertTrue(demoUser.hasCredential(LdifName.userPassword.name(), hashedPassword));
+
+               // search
+               Role[] search = userAdmin.getRoles(null);
+               assertEquals(4, search.length);
+               search = userAdmin.getRoles("(objectClass=groupOfNames)");
+               assertEquals(2, search.length);
+               search = userAdmin.getRoles("(objectclass=inetOrgPerson)");
+               assertEquals(2, search.length);
+               search = userAdmin.getRoles("(&(objectclass=inetOrgPerson)(uid=demo))");
+               assertEquals(1, search.length);
+       }
+
+       public void testReadWriteRead() throws Exception {
+               if (userAdmin instanceof LdifUserAdmin) {
+                       Dictionary<String, Object> props = userAdmin.getProperties();
+                       ByteArrayOutputStream out = new ByteArrayOutputStream();
+                       ((LdifUserAdmin) userAdmin).save(out);
+                       byte[] arr = out.toByteArray();
+                       out.close();
+                       userAdmin.destroy();
+                       // String written = new String(arr);
+                       // System.out.print(written);
+                       try (ByteArrayInputStream in = new ByteArrayInputStream(arr)) {
+                               userAdmin = new LdifUserAdmin(props);
+                               ((LdifUserAdmin) userAdmin).load(in);
+                       }
+                       Role[] search = userAdmin.getRoles(null);
+                       assertEquals(4, search.length);
+               } else {
+                       // test not relevant for LDAP
+               }
+       }
+
+       @Override
+       protected void setUp() throws Exception {
+               Path tempDir = Files.createTempDirectory(getClass().getName());
+               String uriProp = System.getProperty("argeo.userdirectory.uri");
+               if (uriProp != null)
+                       uri = new URI(uriProp);
+               else {
+                       tempDir.toFile().deleteOnExit();
+                       Path ldifPath = tempDir.resolve(BASE_DN + ".ldif");
+                       try (InputStream in = getClass().getResource("basic.ldif").openStream()) {
+                               Files.copy(in, ldifPath);
+                       }
+                       uri = ldifPath.toUri();
+               }
+
+               bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration();
+               tmConf.setServerId(UUID.randomUUID().toString());
+               tmConf.setLogPart1Filename(new File(tempDir.toFile(), "btm1.tlog").getAbsolutePath());
+               tmConf.setLogPart2Filename(new File(tempDir.toFile(), "btm2.tlog").getAbsolutePath());
+               tm = TransactionManagerServices.getTransactionManager();
+
+               userAdmin = initUserAdmin(uri, tm);
+       }
+
+       private AbstractUserDirectory initUserAdmin(URI uri, TransactionManager tm) {
+               Dictionary<String, Object> props = new Hashtable<>();
+               props.put(UserAdminConf.uri.name(), uri.toString());
+               props.put(UserAdminConf.baseDn.name(), BASE_DN);
+               props.put(UserAdminConf.userBase.name(), "ou=users");
+               props.put(UserAdminConf.groupBase.name(), "ou=groups");
+               AbstractUserDirectory userAdmin;
+               if (uri.getScheme().startsWith("ldap"))
+                       userAdmin = new LdapUserAdmin(props);
+               else
+                       userAdmin = new LdifUserAdmin(props);
+               userAdmin.init();
+               // JTA
+               EhCacheXAResourceProducer.registerXAResource(UserDirectory.class.getName(), userAdmin.getXaResource());
+               userAdmin.setTransactionManager(tm);
+               return userAdmin;
+       }
+
+       private void persistAndRestart() {
+               EhCacheXAResourceProducer.unregisterXAResource(UserDirectory.class.getName(), userAdmin.getXaResource());
+               if (userAdmin instanceof LdifUserAdmin)
+                       ((LdifUserAdmin) userAdmin).save();
+               userAdmin.destroy();
+               userAdmin = initUserAdmin(uri, tm);
+       }
+
+       @Override
+       protected void tearDown() throws Exception {
+               EhCacheXAResourceProducer.unregisterXAResource(UserDirectory.class.getName(), userAdmin.getXaResource());
+               tm.shutdown();
+               if (userAdmin != null)
+                       userAdmin.destroy();
+       }
+
+}
diff --git a/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/basic.ldif b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/basic.ldif
new file mode 100644 (file)
index 0000000..b7328b0
--- /dev/null
@@ -0,0 +1,54 @@
+dn: dc=example,dc=com
+objectClass: domain
+objectClass: extensibleObject
+objectClass: top
+dc: example
+
+dn: ou=groups,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: top
+ou: groups
+
+dn: ou=users,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: top
+ou: users
+
+dn: uid=demo,ou=users,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+cn: Demo User
+description: Demo user
+givenName: Demo
+mail: demo@localhost
+sn: User
+uid: demo
+userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
+
+dn: uid=root,ou=users,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: person
+objectClass: organizationalPerson
+objectClass: top
+cn: Super User
+description: Superuser
+givenName: Super
+mail: root@localhost
+sn: User
+uid: root
+userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
+
+dn: cn=admin,ou=groups,dc=example,dc=com
+objectClass: groupOfNames
+objectClass: top
+cn: admin
+member: uid=root,ou=users,dc=example,dc=com
+
+dn: cn=editors,ou=groups,dc=example,dc=com
+objectClass: groupOfNames
+objectClass: top
+cn: editors
+member: cn=admin,ou=groups,dc=example,dc=com
+member: uid=demo,ou=users,dc=example,dc=com
diff --git a/org.argeo.enterprise/pom.xml b/org.argeo.enterprise/pom.xml
new file mode 100644 (file)
index 0000000..b8e8377
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <artifactId>argeo-commons</artifactId>
+               <version>2.1.46-SNAPSHOT</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.enterprise</artifactId>
+       <name>Commons Enterprise</name>
+</project>
\ No newline at end of file
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java
new file mode 100644 (file)
index 0000000..3f5bf85
--- /dev/null
@@ -0,0 +1,426 @@
+package org.argeo.osgi.useradmin;
+
+import static org.argeo.osgi.useradmin.LdifName.inetOrgPerson;
+import static org.argeo.osgi.useradmin.LdifName.objectClass;
+import static org.argeo.osgi.useradmin.LdifName.organizationalPerson;
+import static org.argeo.osgi.useradmin.LdifName.person;
+import static org.argeo.osgi.useradmin.LdifName.top;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.naming.InvalidNameException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/** Base class for a {@link UserDirectory}. */
+public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
+       private final static Log log = LogFactory.getLog(AbstractUserDirectory.class);
+
+       private final Hashtable<String, Object> properties;
+       private final LdapName baseDn;
+       private final String userObjectClass, userBase, groupObjectClass, groupBase;
+
+       private final boolean readOnly;
+       private final URI uri;
+
+       private UserAdmin externalRoles;
+       private List<String> indexedUserProperties = Arrays
+                       .asList(new String[] { LdifName.uid.name(), LdifName.mail.name(), LdifName.cn.name() });
+
+       private String memberAttributeId = "member";
+       private List<String> credentialAttributeIds = Arrays.asList(new String[] { LdifName.userPassword.name() });
+
+       // JTA
+       private TransactionManager transactionManager;
+       private WcXaResource xaResource = new WcXaResource(this);
+
+       public AbstractUserDirectory(Dictionary<String, ?> props) {
+               properties = new Hashtable<String, Object>();
+               for (Enumeration<String> keys = props.keys(); keys.hasMoreElements();) {
+                       String key = keys.nextElement();
+                       properties.put(key, props.get(key));
+               }
+
+               String uriStr = UserAdminConf.uri.getValue(properties);
+               if (uriStr == null)
+                       uri = null;
+               else
+                       try {
+                               uri = new URI(uriStr);
+                       } catch (URISyntaxException e) {
+                               throw new UserDirectoryException("Badly formatted URI " + uriStr, e);
+                       }
+
+               try {
+                       baseDn = new LdapName(UserAdminConf.baseDn.getValue(properties));
+               } catch (InvalidNameException e) {
+                       throw new UserDirectoryException("Badly formated base DN " + UserAdminConf.baseDn.getValue(properties), e);
+               }
+               String readOnlyStr = UserAdminConf.readOnly.getValue(properties);
+               if (readOnlyStr == null) {
+                       readOnly = readOnlyDefault(uri);
+                       properties.put(UserAdminConf.readOnly.name(), Boolean.toString(readOnly));
+               } else
+                       readOnly = new Boolean(readOnlyStr);
+
+               userObjectClass = UserAdminConf.userObjectClass.getValue(properties);
+               userBase = UserAdminConf.userBase.getValue(properties);
+               groupObjectClass = UserAdminConf.groupObjectClass.getValue(properties);
+               groupBase = UserAdminConf.groupBase.getValue(properties);
+       }
+
+       /** Returns the groups this user is a direct member of. */
+       protected abstract List<LdapName> getDirectGroups(LdapName dn);
+
+       protected abstract Boolean daoHasRole(LdapName dn);
+
+       protected abstract DirectoryUser daoGetRole(LdapName key);
+
+       protected abstract List<DirectoryUser> doGetRoles(Filter f);
+
+       public void init() {
+
+       }
+
+       public void destroy() {
+
+       }
+
+       protected boolean isEditing() {
+               return xaResource.wc() != null;
+       }
+
+       protected UserDirectoryWorkingCopy getWorkingCopy() {
+               UserDirectoryWorkingCopy wc = xaResource.wc();
+               if (wc == null)
+                       return null;
+               return wc;
+       }
+
+       protected void checkEdit() {
+               Transaction transaction;
+               try {
+                       transaction = transactionManager.getTransaction();
+               } catch (SystemException e) {
+                       throw new UserDirectoryException("Cannot get transaction", e);
+               }
+               if (transaction == null)
+                       throw new UserDirectoryException("A transaction needs to be active in order to edit");
+               if (xaResource.wc() == null) {
+                       try {
+                               transaction.enlistResource(xaResource);
+                       } catch (Exception e) {
+                               throw new UserDirectoryException("Cannot enlist " + xaResource, e);
+                       }
+               } else {
+               }
+       }
+
+       protected List<Role> getAllRoles(DirectoryUser user) {
+               List<Role> allRoles = new ArrayList<Role>();
+               if (user != null) {
+                       collectRoles(user, allRoles);
+                       allRoles.add(user);
+               } else
+                       collectAnonymousRoles(allRoles);
+               return allRoles;
+       }
+
+       private void collectRoles(DirectoryUser user, List<Role> allRoles) {
+               for (LdapName groupDn : getDirectGroups(user.getDn())) {
+                       // TODO check for loops
+                       DirectoryUser group = doGetRole(groupDn);
+                       allRoles.add(group);
+                       collectRoles(group, allRoles);
+               }
+       }
+
+       private void collectAnonymousRoles(List<Role> allRoles) {
+               // TODO gather anonymous roles
+       }
+
+       // USER ADMIN
+       @Override
+       public Role getRole(String name) {
+               return doGetRole(toDn(name));
+       }
+
+       protected DirectoryUser doGetRole(LdapName dn) {
+               UserDirectoryWorkingCopy wc = getWorkingCopy();
+               DirectoryUser user = daoGetRole(dn);
+               if (wc != null) {
+                       if (user == null && wc.getNewUsers().containsKey(dn))
+                               user = wc.getNewUsers().get(dn);
+                       else if (wc.getDeletedUsers().containsKey(dn))
+                               user = null;
+               }
+               return user;
+       }
+
+       @SuppressWarnings("unchecked")
+       @Override
+       public Role[] getRoles(String filter) throws InvalidSyntaxException {
+               UserDirectoryWorkingCopy wc = getWorkingCopy();
+               Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null;
+               List<DirectoryUser> res = doGetRoles(f);
+               if (wc != null) {
+                       for (Iterator<DirectoryUser> it = res.iterator(); it.hasNext();) {
+                               DirectoryUser user = it.next();
+                               LdapName dn = user.getDn();
+                               if (wc.getDeletedUsers().containsKey(dn))
+                                       it.remove();
+                       }
+                       for (DirectoryUser user : wc.getNewUsers().values()) {
+                               if (f == null || f.match(user.getProperties()))
+                                       res.add(user);
+                       }
+                       // no need to check modified users,
+                       // since doGetRoles was already based on the modified attributes
+               }
+               return res.toArray(new Role[res.size()]);
+       }
+
+       @Override
+       public User getUser(String key, String value) {
+               // TODO check value null or empty
+               List<DirectoryUser> collectedUsers = new ArrayList<DirectoryUser>(getIndexedUserProperties().size());
+               if (key != null) {
+                       doGetUser(key, value, collectedUsers);
+               } else {
+                       // try dn
+                       DirectoryUser user = null;
+                       try {
+                               user = (DirectoryUser) getRole(value);
+                               if (user != null)
+                                       collectedUsers.add(user);
+                       } catch (Exception e) {
+                               // silent
+                       }
+                       // try all indexes
+                       for (String attr : getIndexedUserProperties())
+                               doGetUser(attr, value, collectedUsers);
+               }
+               if (collectedUsers.size() == 1)
+                       return collectedUsers.get(0);
+               else if (collectedUsers.size() > 1)
+                       log.warn(collectedUsers.size() + " users for " + (key != null ? key + "=" : "") + value);
+               return null;
+       }
+
+       protected void doGetUser(String key, String value, List<DirectoryUser> collectedUsers) {
+               try {
+                       Filter f = FrameworkUtil.createFilter("(" + key + "=" + value + ")");
+                       List<DirectoryUser> users = doGetRoles(f);
+                       collectedUsers.addAll(users);
+               } catch (InvalidSyntaxException e) {
+                       throw new UserDirectoryException("Cannot get user with " + key + "=" + value, e);
+               }
+       }
+
+       @Override
+       public Authorization getAuthorization(User user) {
+               return new LdifAuthorization((DirectoryUser) user, getAllRoles((DirectoryUser) user));
+       }
+
+       @Override
+       public Role createRole(String name, int type) {
+               checkEdit();
+               UserDirectoryWorkingCopy wc = getWorkingCopy();
+               LdapName dn = toDn(name);
+               if ((daoHasRole(dn) && !wc.getDeletedUsers().containsKey(dn)) || wc.getNewUsers().containsKey(dn))
+                       throw new UserDirectoryException("Already a role " + name);
+               BasicAttributes attrs = new BasicAttributes(true);
+               // attrs.put(LdifName.dn.name(), dn.toString());
+               Rdn nameRdn = dn.getRdn(dn.size() - 1);
+               // TODO deal with multiple attr RDN
+               attrs.put(nameRdn.getType(), nameRdn.getValue());
+               if (wc.getDeletedUsers().containsKey(dn)) {
+                       wc.getDeletedUsers().remove(dn);
+                       wc.getModifiedUsers().put(dn, attrs);
+               } else {
+                       wc.getModifiedUsers().put(dn, attrs);
+                       DirectoryUser newRole = newRole(dn, type, attrs);
+                       wc.getNewUsers().put(dn, newRole);
+               }
+               return getRole(name);
+       }
+
+       protected DirectoryUser newRole(LdapName dn, int type, Attributes attrs) {
+               LdifUser newRole;
+               BasicAttribute objClass = new BasicAttribute(objectClass.name());
+               if (type == Role.USER) {
+                       String userObjClass = newUserObjectClass(dn);
+                       objClass.add(userObjClass);
+                       if (inetOrgPerson.name().equals(userObjClass)) {
+                               objClass.add(organizationalPerson.name());
+                               objClass.add(person.name());
+                       } else if (organizationalPerson.name().equals(userObjClass)) {
+                               objClass.add(person.name());
+                       }
+                       objClass.add(top.name());
+                       attrs.put(objClass);
+                       newRole = new LdifUser(this, dn, attrs);
+               } else if (type == Role.GROUP) {
+                       String groupObjClass = getGroupObjectClass();
+                       objClass.add(groupObjClass);
+                       // objClass.add(LdifName.extensibleObject.name());
+                       objClass.add(top.name());
+                       attrs.put(objClass);
+                       newRole = new LdifGroup(this, dn, attrs);
+               } else
+                       throw new UserDirectoryException("Unsupported type " + type);
+               return newRole;
+       }
+
+       @Override
+       public boolean removeRole(String name) {
+               checkEdit();
+               UserDirectoryWorkingCopy wc = getWorkingCopy();
+               LdapName dn = toDn(name);
+               boolean actuallyDeleted;
+               if (daoHasRole(dn) || wc.getNewUsers().containsKey(dn)) {
+                       DirectoryUser user = (DirectoryUser) getRole(name);
+                       wc.getDeletedUsers().put(dn, user);
+                       actuallyDeleted = true;
+               } else {// just removing from groups (e.g. system roles)
+                       actuallyDeleted = false;
+               }
+               for (LdapName groupDn : getDirectGroups(dn)) {
+                       DirectoryUser group = doGetRole(groupDn);
+                       group.getAttributes().get(getMemberAttributeId()).remove(dn.toString());
+               }
+               return actuallyDeleted;
+       }
+
+       // TRANSACTION
+       protected void prepare(UserDirectoryWorkingCopy wc) {
+
+       }
+
+       protected void commit(UserDirectoryWorkingCopy wc) {
+
+       }
+
+       protected void rollback(UserDirectoryWorkingCopy wc) {
+
+       }
+
+       // UTILITIES
+       protected LdapName toDn(String name) {
+               try {
+                       return new LdapName(name);
+               } catch (InvalidNameException e) {
+                       throw new UserDirectoryException("Badly formatted name", e);
+               }
+       }
+
+       // GETTERS
+       protected String getMemberAttributeId() {
+               return memberAttributeId;
+       }
+
+       protected List<String> getCredentialAttributeIds() {
+               return credentialAttributeIds;
+       }
+
+       protected URI getUri() {
+               return uri;
+       }
+
+       protected List<String> getIndexedUserProperties() {
+               return indexedUserProperties;
+       }
+
+       protected void setIndexedUserProperties(List<String> indexedUserProperties) {
+               this.indexedUserProperties = indexedUserProperties;
+       }
+
+       private static boolean readOnlyDefault(URI uri) {
+               if (uri == null)
+                       return true;
+               if (uri.getScheme().equals("file")) {
+                       File file = new File(uri);
+                       if (file.exists())
+                               return !file.canWrite();
+                       else
+                               return !file.getParentFile().canWrite();
+               }
+               return true;
+       }
+
+       public boolean isReadOnly() {
+               return readOnly;
+       }
+
+       protected UserAdmin getExternalRoles() {
+               return externalRoles;
+       }
+
+       public LdapName getBaseDn() {
+               // always clone so that the property is not modified by reference
+               return (LdapName) baseDn.clone();
+       }
+
+       /** dn can be null, in that case a default should be returned. */
+       public String getUserObjectClass() {
+               return userObjectClass;
+       }
+
+       public String getUserBase() {
+               return userBase;
+       }
+
+       protected String newUserObjectClass(LdapName dn) {
+               return getUserObjectClass();
+       }
+
+       public String getGroupObjectClass() {
+               return groupObjectClass;
+       }
+
+       public String getGroupBase() {
+               return groupBase;
+       }
+
+       public Dictionary<String, Object> getProperties() {
+               return properties;
+       }
+
+       public void setExternalRoles(UserAdmin externalRoles) {
+               this.externalRoles = externalRoles;
+       }
+
+       public void setTransactionManager(TransactionManager transactionManager) {
+               this.transactionManager = transactionManager;
+       }
+
+       public WcXaResource getXaResource() {
+               return xaResource;
+       }
+
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java
new file mode 100644 (file)
index 0000000..f270b8d
--- /dev/null
@@ -0,0 +1,73 @@
+package org.argeo.osgi.useradmin;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.osgi.service.useradmin.Authorization;
+
+class AggregatingAuthorization implements Authorization {
+       private final String name;
+       private final String displayName;
+       private final List<String> systemRoles;
+       private final List<String> roles;
+
+       public AggregatingAuthorization(String name, String displayName,
+                       Collection<String> systemRoles, String[] roles) {
+               this.name = new X500Principal(name).getName();
+               this.displayName = displayName;
+               this.systemRoles = Collections.unmodifiableList(new ArrayList<String>(
+                               systemRoles));
+               this.roles = Collections.unmodifiableList(Arrays.asList(roles));
+       }
+
+       @Override
+       public String getName() {
+               return name;
+       }
+
+       @Override
+       public boolean hasRole(String name) {
+               if (systemRoles.contains(name))
+                       return true;
+               if (roles.contains(name))
+                       return true;
+               return false;
+       }
+
+       @Override
+       public String[] getRoles() {
+               int size = systemRoles.size() + roles.size();
+               List<String> res = new ArrayList<String>(size);
+               res.addAll(systemRoles);
+               res.addAll(roles);
+               return res.toArray(new String[size]);
+       }
+
+       @Override
+       public int hashCode() {
+               if (name == null)
+                       return super.hashCode();
+               return name.hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (!(obj instanceof Authorization))
+                       return false;
+               Authorization that = (Authorization) obj;
+               if (name == null)
+                       return that.getName() == null;
+               return name.equals(that.getName());
+       }
+
+       @Override
+       public String toString() {
+               return displayName;
+       }
+
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java
new file mode 100644 (file)
index 0000000..860c5ef
--- /dev/null
@@ -0,0 +1,189 @@
+package org.argeo.osgi.useradmin;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * Aggregates multiple {@link UserDirectory} and integrates them with system
+ * roles.
+ */
+public class AggregatingUserAdmin implements UserAdmin {
+       private final LdapName systemRolesBaseDn;
+
+       // DAOs
+       private AbstractUserDirectory systemRoles = null;
+       private Map<LdapName, AbstractUserDirectory> businessRoles = new HashMap<LdapName, AbstractUserDirectory>();
+
+       public AggregatingUserAdmin(String systemRolesBaseDn) {
+               try {
+                       this.systemRolesBaseDn = new LdapName(systemRolesBaseDn);
+               } catch (InvalidNameException e) {
+                       throw new UserDirectoryException("Cannot initialize " + AggregatingUserAdmin.class, e);
+               }
+       }
+
+       @Override
+       public Role createRole(String name, int type) {
+               return findUserAdmin(name).createRole(name, type);
+       }
+
+       @Override
+       public boolean removeRole(String name) {
+               boolean actuallyDeleted = findUserAdmin(name).removeRole(name);
+               systemRoles.removeRole(name);
+               return actuallyDeleted;
+       }
+
+       @Override
+       public Role getRole(String name) {
+               return findUserAdmin(name).getRole(name);
+       }
+
+       @Override
+       public Role[] getRoles(String filter) throws InvalidSyntaxException {
+               List<Role> res = new ArrayList<Role>();
+               for (UserAdmin userAdmin : businessRoles.values()) {
+                       res.addAll(Arrays.asList(userAdmin.getRoles(filter)));
+               }
+               res.addAll(Arrays.asList(systemRoles.getRoles(filter)));
+               return res.toArray(new Role[res.size()]);
+       }
+
+       @Override
+       public User getUser(String key, String value) {
+               List<User> res = new ArrayList<User>();
+               for (UserAdmin userAdmin : businessRoles.values()) {
+                       User u = userAdmin.getUser(key, value);
+                       if (u != null)
+                               res.add(u);
+               }
+               // Note: node roles cannot contain users, so it is not searched
+               return res.size() == 1 ? res.get(0) : null;
+       }
+
+       @Override
+       public Authorization getAuthorization(User user) {
+               if (user == null) {// anonymous
+                       return systemRoles.getAuthorization(null);
+               }
+               UserAdmin userAdmin = findUserAdmin(user.getName());
+               Authorization rawAuthorization = userAdmin.getAuthorization(user);
+               // gather system roles
+               Set<String> sysRoles = new HashSet<String>();
+               for (String role : rawAuthorization.getRoles()) {
+                       Authorization auth = systemRoles.getAuthorization((User) userAdmin.getRole(role));
+                       sysRoles.addAll(Arrays.asList(auth.getRoles()));
+               }
+               Authorization authorization = new AggregatingAuthorization(rawAuthorization.getName(),
+                               rawAuthorization.toString(), sysRoles, rawAuthorization.getRoles());
+               return authorization;
+       }
+
+       //
+       // USER ADMIN AGGREGATOR
+       //
+       protected void addUserDirectory(AbstractUserDirectory userDirectory) {
+               LdapName baseDn = userDirectory.getBaseDn();
+               if (isSystemRolesBaseDn(baseDn)) {
+                       this.systemRoles = userDirectory;
+                       systemRoles.setExternalRoles(this);
+               } else {
+                       if (businessRoles.containsKey(baseDn))
+                               throw new UserDirectoryException("There is already a user admin for " + baseDn);
+                       businessRoles.put(baseDn, userDirectory);
+               }
+               userDirectory.init();
+               postAdd(userDirectory);
+       }
+
+       /** Called after a new user directory has been added */
+       protected void postAdd(AbstractUserDirectory userDirectory) {
+       }
+
+       private UserAdmin findUserAdmin(String name) {
+               try {
+                       return findUserAdmin(new LdapName(name));
+               } catch (InvalidNameException e) {
+                       throw new UserDirectoryException("Badly formatted name " + name, e);
+               }
+       }
+
+       private UserAdmin findUserAdmin(LdapName name) {
+               if (name.startsWith(systemRolesBaseDn))
+                       return systemRoles;
+               List<UserAdmin> res = new ArrayList<UserAdmin>(1);
+               for (LdapName baseDn : businessRoles.keySet()) {
+                       if (name.startsWith(baseDn))
+                               res.add(businessRoles.get(baseDn));
+               }
+               if (res.size() == 0)
+                       throw new UserDirectoryException("Cannot find user admin for " + name);
+               if (res.size() > 1)
+                       throw new UserDirectoryException("Multiple user admin found for " + name);
+               return res.get(0);
+       }
+
+       protected boolean isSystemRolesBaseDn(LdapName baseDn) {
+               return baseDn.equals(systemRolesBaseDn);
+       }
+
+       protected Dictionary<String, Object> currentState() {
+               Dictionary<String, Object> res = new Hashtable<String, Object>();
+               // res.put(NodeConstants.CN, NodeConstants.DEFAULT);
+               for (LdapName name : businessRoles.keySet()) {
+                       AbstractUserDirectory userDirectory = businessRoles.get(name);
+                       String uri = UserAdminConf.propertiesAsUri(userDirectory.getProperties()).toString();
+                       res.put(uri, "");
+               }
+               return res;
+       }
+
+       public void destroy() {
+               for (LdapName name : businessRoles.keySet()) {
+                       AbstractUserDirectory userDirectory = businessRoles.get(name);
+                       destroy(userDirectory);
+               }
+               businessRoles.clear();
+               businessRoles = null;
+               destroy(systemRoles);
+               systemRoles = null;
+       }
+
+       private void destroy(AbstractUserDirectory userDirectory) {
+               preDestroy(userDirectory);
+               userDirectory.destroy();
+       }
+
+       protected void removeUserDirectory(LdapName baseDn) {
+               if (isSystemRolesBaseDn(baseDn))
+                       throw new UserDirectoryException("System roles cannot be removed ");
+               if (!businessRoles.containsKey(baseDn))
+                       throw new UserDirectoryException("No user directory registered for " + baseDn);
+               AbstractUserDirectory userDirectory = businessRoles.remove(baseDn);
+               destroy(userDirectory);
+       }
+
+       /**
+        * Called before each user directory is destroyed, so that additional
+        * actions can be performed.
+        */
+       protected void preDestroy(UserDirectory userDirectory) {
+       }
+
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DigestUtils.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DigestUtils.java
new file mode 100644 (file)
index 0000000..d8f8ce9
--- /dev/null
@@ -0,0 +1,20 @@
+package org.argeo.osgi.useradmin;
+
+import java.security.MessageDigest;
+
+class DigestUtils {
+       static byte[] sha1(byte[] bytes) {
+               try {
+                       MessageDigest digest = MessageDigest.getInstance("SHA1");
+                       digest.update(bytes);
+                       byte[] checksum = digest.digest();
+                       return checksum;
+               } catch (Exception e) {
+                       throw new UserDirectoryException("Cannot SHA1 digest", e);
+               }
+       }
+
+       private DigestUtils() {
+       }
+
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DirectoryGroup.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DirectoryGroup.java
new file mode 100644 (file)
index 0000000..7f80463
--- /dev/null
@@ -0,0 +1,12 @@
+package org.argeo.osgi.useradmin;
+
+import java.util.List;
+
+import javax.naming.ldap.LdapName;
+
+import org.osgi.service.useradmin.Group;
+
+/** A group in a user directroy. */
+interface DirectoryGroup extends Group, DirectoryUser {
+       List<LdapName> getMemberNames();
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DirectoryUser.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/DirectoryUser.java
new file mode 100644 (file)
index 0000000..146b805
--- /dev/null
@@ -0,0 +1,15 @@
+package org.argeo.osgi.useradmin;
+
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapName;
+
+import org.osgi.service.useradmin.User;
+
+/** A user in a user directory. */
+interface DirectoryUser extends User {
+       LdapName getDn();
+
+       Attributes getAttributes();
+
+       void publishAttributes(Attributes modifiedAttributes);
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdapUserAdmin.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdapUserAdmin.java
new file mode 100644 (file)
index 0000000..7a617df
--- /dev/null
@@ -0,0 +1,227 @@
+package org.argeo.osgi.useradmin;
+
+import static org.argeo.osgi.useradmin.LdifName.objectClass;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.naming.Binding;
+import javax.naming.Context;
+import javax.naming.InvalidNameException;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapName;
+import javax.transaction.TransactionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.Filter;
+
+/**
+ * A user admin based on a LDAP server. Requires a {@link TransactionManager}
+ * and an open transaction for write access.
+ */
+public class LdapUserAdmin extends AbstractUserDirectory {
+       private final static Log log = LogFactory.getLog(LdapUserAdmin.class);
+
+       private InitialLdapContext initialLdapContext = null;
+
+       public LdapUserAdmin(Dictionary<String, ?> properties) {
+               super(properties);
+               try {
+                       Hashtable<String, Object> connEnv = new Hashtable<String, Object>();
+                       connEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+                       connEnv.put(Context.PROVIDER_URL, getUri().toString());
+                       connEnv.put("java.naming.ldap.attributes.binary", LdifName.userPassword.name());
+
+                       initialLdapContext = new InitialLdapContext(connEnv, null);
+                       // StartTlsResponse tls = (StartTlsResponse) ctx
+                       // .extendedOperation(new StartTlsRequest());
+                       // tls.negotiate();
+                       initialLdapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
+                       Object principal = properties.get(Context.SECURITY_PRINCIPAL);
+                       if (principal != null) {
+                               initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, principal.toString());
+                               Object creds = properties.get(Context.SECURITY_CREDENTIALS);
+                               if (creds != null) {
+                                       initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, creds.toString());
+
+                               }
+                       }
+                       // initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL,
+                       // "uid=admin,ou=system");
+                       // initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS,
+                       // "secret");
+               } catch (Exception e) {
+                       throw new UserDirectoryException("Cannot connect to LDAP", e);
+               }
+       }
+
+       public void destroy() {
+               try {
+                       // tls.close();
+                       initialLdapContext.close();
+               } catch (NamingException e) {
+                       log.error("Cannot destroy LDAP user admin", e);
+               }
+       }
+
+       protected InitialLdapContext getLdapContext() {
+               return initialLdapContext;
+       }
+
+       @Override
+       protected Boolean daoHasRole(LdapName dn) {
+               return daoGetRole(dn) != null;
+       }
+
+       @Override
+       protected DirectoryUser daoGetRole(LdapName name) {
+               try {
+                       Attributes attrs = getLdapContext().getAttributes(name);
+                       if (attrs.size() == 0)
+                               return null;
+                       LdifUser res;
+                       if (attrs.get(objectClass.name()).contains(getGroupObjectClass()))
+                               res = new LdifGroup(this, name, attrs);
+                       else if (attrs.get(objectClass.name()).contains(getUserObjectClass()))
+                               res = new LdifUser(this, name, attrs);
+                       else
+                               throw new UserDirectoryException("Unsupported LDAP type for " + name);
+                       return res;
+               } catch (NamingException e) {
+                       return null;
+               }
+       }
+
+       @Override
+       protected List<DirectoryUser> doGetRoles(Filter f) {
+               try {
+                       String searchFilter = f != null ? f.toString()
+                                       : "(|(" + objectClass + "=" + getUserObjectClass() + ")(" + objectClass + "="
+                                                       + getGroupObjectClass() + "))";
+                       SearchControls searchControls = new SearchControls();
+                       searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+                       LdapName searchBase = getBaseDn();
+                       NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
+
+                       ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
+                       results: while (results.hasMoreElements()) {
+                               SearchResult searchResult = results.next();
+                               Attributes attrs = searchResult.getAttributes();
+                               Attribute objectClassAttr = attrs.get(objectClass.name());
+                               LdapName dn = toDn(searchBase, searchResult);
+                               LdifUser role;
+                               if (objectClassAttr.contains(getGroupObjectClass()))
+                                       role = new LdifGroup(this, dn, attrs);
+                               else if (objectClassAttr.contains(getUserObjectClass()))
+                                       role = new LdifUser(this, dn, attrs);
+                               else {
+                                       log.warn("Unsupported LDAP type for " + searchResult.getName());
+                                       continue results;
+                               }
+                               res.add(role);
+                       }
+                       return res;
+               } catch (Exception e) {
+                       throw new UserDirectoryException("Cannot get roles for filter " + f, e);
+               }
+       }
+
+       private LdapName toDn(LdapName baseDn, Binding binding) throws InvalidNameException {
+               return new LdapName(binding.isRelative() ? binding.getName() + "," + baseDn : binding.getName());
+       }
+
+       @Override
+       protected List<LdapName> getDirectGroups(LdapName dn) {
+               List<LdapName> directGroups = new ArrayList<LdapName>();
+               try {
+                       String searchFilter = "(&(" + objectClass + "=" + getGroupObjectClass() + ")(" + getMemberAttributeId()
+                                       + "=" + dn + "))";
+
+                       SearchControls searchControls = new SearchControls();
+                       searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+                       LdapName searchBase = getBaseDn();
+                       NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
+
+                       while (results.hasMoreElements()) {
+                               SearchResult searchResult = (SearchResult) results.nextElement();
+                               directGroups.add(toDn(searchBase, searchResult));
+                       }
+                       return directGroups;
+               } catch (Exception e) {
+                       throw new UserDirectoryException("Cannot populate direct members of " + dn, e);
+               }
+       }
+
+       @Override
+       protected void prepare(UserDirectoryWorkingCopy wc) {
+               try {
+                       getLdapContext().reconnect(getLdapContext().getConnectControls());
+                       // delete
+                       for (LdapName dn : wc.getDeletedUsers().keySet()) {
+                               if (!entryExists(dn))
+                                       throw new UserDirectoryException("User to delete no found " + dn);
+                       }
+                       // add
+                       for (LdapName dn : wc.getNewUsers().keySet()) {
+                               if (entryExists(dn))
+                                       throw new UserDirectoryException("User to create found " + dn);
+                       }
+                       // modify
+                       for (LdapName dn : wc.getModifiedUsers().keySet()) {
+                               if (!wc.getNewUsers().containsKey(dn) && !entryExists(dn))
+                                       throw new UserDirectoryException("User to modify not found " + dn);
+                       }
+               } catch (NamingException e) {
+                       throw new UserDirectoryException("Cannot prepare LDAP", e);
+               }
+       }
+
+       private boolean entryExists(LdapName dn) throws NamingException {
+               try {
+                       return getLdapContext().getAttributes(dn).size() != 0;
+               } catch (NameNotFoundException e) {
+                       return false;
+               }
+       }
+
+       @Override
+       protected void commit(UserDirectoryWorkingCopy wc) {
+               try {
+                       // delete
+                       for (LdapName dn : wc.getDeletedUsers().keySet()) {
+                               getLdapContext().destroySubcontext(dn);
+                       }
+                       // add
+                       for (LdapName dn : wc.getNewUsers().keySet()) {
+                               DirectoryUser user = wc.getNewUsers().get(dn);
+                               getLdapContext().createSubcontext(dn, user.getAttributes());
+                       }
+                       // modify
+                       for (LdapName dn : wc.getModifiedUsers().keySet()) {
+                               Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
+                               getLdapContext().modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, modifiedAttrs);
+                       }
+               } catch (NamingException e) {
+                       throw new UserDirectoryException("Cannot commit LDAP", e);
+               }
+       }
+
+       @Override
+       protected void rollback(UserDirectoryWorkingCopy wc) {
+               // prepare not impacting
+       }
+
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifAuthorization.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifAuthorization.java
new file mode 100644 (file)
index 0000000..e06c42e
--- /dev/null
@@ -0,0 +1,82 @@
+package org.argeo.osgi.useradmin;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+/** Basic authorization. */
+class LdifAuthorization implements Authorization {
+       private final String name;
+       private final String displayName;
+       private final List<String> allRoles;
+
+       @SuppressWarnings("unchecked")
+       public LdifAuthorization(User user, List<Role> allRoles) {
+               if (user == null) {
+                       this.name = null;
+                       this.displayName = "anonymous";
+               } else {
+                       this.name = user.getName();
+                       Dictionary<String, Object> props = user.getProperties();
+                       Object displayName = props.get(LdifName.displayName);
+                       if (displayName == null)
+                               displayName = props.get(LdifName.cn);
+                       if (displayName == null)
+                               displayName = props.get(LdifName.uid);
+                       if (displayName == null)
+                               displayName = user.getName();
+                       if (displayName == null)
+                               throw new UserDirectoryException("Cannot set display name for "
+                                               + user);
+                       this.displayName = displayName.toString();
+               }
+               // roles
+               String[] roles = new String[allRoles.size()];
+               for (int i = 0; i < allRoles.size(); i++) {
+                       roles[i] = allRoles.get(i).getName();
+               }
+               this.allRoles = Collections.unmodifiableList(Arrays.asList(roles));
+       }
+
+       @Override
+       public String getName() {
+               return name;
+       }
+
+       @Override
+       public boolean hasRole(String name) {
+               return allRoles.contains(name);
+       }
+
+       @Override
+       public String[] getRoles() {
+               return allRoles.toArray(new String[allRoles.size()]);
+       }
+
+       @Override
+       public int hashCode() {
+               if (name == null)
+                       return super.hashCode();
+               return name.hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (!(obj instanceof Authorization))
+                       return false;
+               Authorization that = (Authorization) obj;
+               if (name == null)
+                       return that.getName() == null;
+               return name.equals(that.getName());
+       }
+
+       @Override
+       public String toString() {
+               return displayName;
+       }
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifGroup.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifGroup.java
new file mode 100644 (file)
index 0000000..bd12911
--- /dev/null
@@ -0,0 +1,106 @@
+package org.argeo.osgi.useradmin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapName;
+
+import org.osgi.service.useradmin.Role;
+
+/** Directory group implementation */
+class LdifGroup extends LdifUser implements DirectoryGroup {
+       private final String memberAttributeId;
+
+       LdifGroup(AbstractUserDirectory userAdmin, LdapName dn,
+                       Attributes attributes) {
+               super(userAdmin, dn, attributes);
+               memberAttributeId = userAdmin.getMemberAttributeId();
+       }
+
+       @Override
+       public boolean addMember(Role role) {
+               getUserAdmin().checkEdit();
+               if (!isEditing())
+                       startEditing();
+
+               Attribute member = getAttributes().get(memberAttributeId);
+               if (member != null) {
+                       if (member.contains(role.getName()))
+                               return false;
+                       else
+                               member.add(role.getName());
+               } else
+                       getAttributes().put(memberAttributeId, role.getName());
+               return true;
+       }
+
+       @Override
+       public boolean addRequiredMember(Role role) {
+               throw new UnsupportedOperationException();
+       }
+
+       @Override
+       public boolean removeMember(Role role) {
+               getUserAdmin().checkEdit();
+               if (!isEditing())
+                       startEditing();
+
+               Attribute member = getAttributes().get(memberAttributeId);
+               if (member != null) {
+                       if (!member.contains(role.getName()))
+                               return false;
+                       member.remove(role.getName());
+                       return true;
+               } else
+                       return false;
+       }
+
+       @Override
+       public Role[] getMembers() {
+               List<Role> directMembers = new ArrayList<Role>();
+               for (LdapName ldapName : getMemberNames()) {
+                       Role role = getUserAdmin().getRole(ldapName.toString());
+                       if (role == null) {
+                               if (getUserAdmin().getExternalRoles() != null)
+                                       role = getUserAdmin().getExternalRoles().getRole(
+                                                       ldapName.toString());
+                       }
+                       if (role == null)
+                               throw new UserDirectoryException("No role found for "
+                                               + ldapName);
+                       directMembers.add(role);
+               }
+               return directMembers.toArray(new Role[directMembers.size()]);
+       }
+
+       @Override
+       public List<LdapName> getMemberNames() {
+               Attribute memberAttribute = getAttributes().get(memberAttributeId);
+               if (memberAttribute == null)
+                       return new ArrayList<LdapName>();
+               try {
+                       List<LdapName> roles = new ArrayList<LdapName>();
+                       NamingEnumeration<?> values = memberAttribute.getAll();
+                       while (values.hasMore()) {
+                               LdapName dn = new LdapName(values.next().toString());
+                               roles.add(dn);
+                       }
+                       return roles;
+               } catch (Exception e) {
+                       throw new UserDirectoryException("Cannot get members", e);
+               }
+       }
+
+       @Override
+       public Role[] getRequiredMembers() {
+               throw new UnsupportedOperationException();
+       }
+
+       @Override
+       public int getType() {
+               return GROUP;
+       }
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifName.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifName.java
new file mode 100644 (file)
index 0000000..919e507
--- /dev/null
@@ -0,0 +1,26 @@
+package org.argeo.osgi.useradmin;
+
+import javax.naming.ldap.LdapName;
+
+/**
+ * Standard LDAP attributes and object classes leveraged in this implementation
+ * of user admin. Named {@link LdifName} in order not to collide with
+ * {@link LdapName}.
+ */
+public enum LdifName {
+       // Attributes
+       dn, dc, cn, sn, uid, mail, displayName, objectClass, userPassword, givenName, description, member,
+       // Object classes
+       inetOrgPerson, organizationalPerson, person, groupOfNames, groupOfUniqueNames, top;
+
+       public final static String PREFIX = "ldap:";
+
+       /** For use as XML name. */
+       public String property() {
+               return PREFIX + name();
+       }
+
+       public static LdifName local(String property) {
+               return LdifName.valueOf(property.substring(PREFIX.length()));
+       }
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java
new file mode 100644 (file)
index 0000000..866c48c
--- /dev/null
@@ -0,0 +1,363 @@
+package org.argeo.osgi.useradmin;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.ldap.LdapName;
+
+/** Directory user implementation */
+class LdifUser implements DirectoryUser {
+       private final AbstractUserDirectory userAdmin;
+
+       private final LdapName dn;
+
+       private final boolean frozen;
+       private Attributes publishedAttributes;
+
+       private final AttributeDictionary properties;
+       private final AttributeDictionary credentials;
+
+       LdifUser(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes) {
+               this(userAdmin, dn, attributes, false);
+       }
+
+       private LdifUser(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes, boolean frozen) {
+               this.userAdmin = userAdmin;
+               this.dn = dn;
+               this.publishedAttributes = attributes;
+               properties = new AttributeDictionary(false);
+               credentials = new AttributeDictionary(true);
+               this.frozen = frozen;
+       }
+
+       @Override
+       public String getName() {
+               return dn.toString();
+       }
+
+       @Override
+       public int getType() {
+               return USER;
+       }
+
+       @Override
+       public Dictionary<String, Object> getProperties() {
+               return properties;
+       }
+
+       @Override
+       public Dictionary<String, Object> getCredentials() {
+               return credentials;
+       }
+
+       @Override
+       public boolean hasCredential(String key, Object value) {
+               if (key == null) {
+                       // TODO check other sources (like PKCS12)
+                       char[] password = toChars(value);
+                       byte[] hashedPassword = hash(password);
+                       return hasCredential(LdifName.userPassword.name(), hashedPassword);
+               }
+
+               Object storedValue = getCredentials().get(key);
+               if (storedValue == null || value == null)
+                       return false;
+               if (!(value instanceof String || value instanceof byte[]))
+                       return false;
+               if (storedValue instanceof String && value instanceof String)
+                       return storedValue.equals(value);
+               if (storedValue instanceof byte[] && value instanceof byte[])
+                       return Arrays.equals((byte[]) storedValue, (byte[]) value);
+               return false;
+       }
+
+       /** Hash and clear the password */
+       private byte[] hash(char[] password) {
+               byte[] hashedPassword = ("{SHA}" + Base64.getEncoder().encodeToString(DigestUtils.sha1(toBytes(password))))
+                               .getBytes();
+               Arrays.fill(password, '\u0000');
+               return hashedPassword;
+       }
+
+       private byte[] toBytes(char[] chars) {
+               CharBuffer charBuffer = CharBuffer.wrap(chars);
+               ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
+               byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
+               Arrays.fill(charBuffer.array(), '\u0000'); // clear sensitive data
+               Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data
+               return bytes;
+       }
+
+       private char[] toChars(Object obj) {
+               if (obj instanceof char[])
+                       return (char[]) obj;
+               if (!(obj instanceof byte[]))
+                       throw new IllegalArgumentException(obj.getClass() + " is not a byte array");
+               ByteBuffer fromBuffer = ByteBuffer.wrap((byte[]) obj);
+               CharBuffer toBuffer = Charset.forName("UTF-8").decode(fromBuffer);
+               char[] res = Arrays.copyOfRange(toBuffer.array(), toBuffer.position(), toBuffer.limit());
+               Arrays.fill(fromBuffer.array(), (byte) 0); // clear sensitive data
+               Arrays.fill((byte[]) obj, (byte) 0); // clear sensitive data
+               Arrays.fill(toBuffer.array(), '\u0000'); // clear sensitive data
+               return res;
+       }
+
+       @Override
+       public LdapName getDn() {
+               return dn;
+       }
+
+       @Override
+       public synchronized Attributes getAttributes() {
+               return isEditing() ? getModifiedAttributes() : publishedAttributes;
+       }
+
+       /** Should only be called from working copy thread. */
+       private synchronized Attributes getModifiedAttributes() {
+               assert getWc() != null;
+               return getWc().getAttributes(getDn());
+       }
+
+       protected synchronized boolean isEditing() {
+               return getWc() != null && getModifiedAttributes() != null;
+       }
+
+       private synchronized UserDirectoryWorkingCopy getWc() {
+               return userAdmin.getWorkingCopy();
+       }
+
+       protected synchronized void startEditing() {
+               if (frozen)
+                       throw new UserDirectoryException("Cannot edit frozen view");
+               if (getUserAdmin().isReadOnly())
+                       throw new UserDirectoryException("User directory is read-only");
+               assert getModifiedAttributes() == null;
+               getWc().startEditing(this);
+               // modifiedAttributes = (Attributes) publishedAttributes.clone();
+       }
+
+       public synchronized void publishAttributes(Attributes modifiedAttributes) {
+               publishedAttributes = modifiedAttributes;
+       }
+
+       public DirectoryUser getPublished() {
+               return new LdifUser(userAdmin, dn, publishedAttributes, true);
+       }
+
+       @Override
+       public int hashCode() {
+               return dn.hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj instanceof LdifUser) {
+                       LdifUser that = (LdifUser) obj;
+                       return this.dn.equals(that.dn);
+               }
+               return false;
+       }
+
+       @Override
+       public String toString() {
+               return dn.toString();
+       }
+
+       protected AbstractUserDirectory getUserAdmin() {
+               return userAdmin;
+       }
+
+       private class AttributeDictionary extends Dictionary<String, Object> {
+               private final List<String> effectiveKeys = new ArrayList<String>();
+               private final List<String> attrFilter;
+               private final Boolean includeFilter;
+
+               public AttributeDictionary(Boolean includeFilter) {
+                       this.attrFilter = userAdmin.getCredentialAttributeIds();
+                       this.includeFilter = includeFilter;
+                       try {
+                               NamingEnumeration<String> ids = getAttributes().getIDs();
+                               while (ids.hasMore()) {
+                                       String id = ids.next();
+                                       if (includeFilter && attrFilter.contains(id))
+                                               effectiveKeys.add(id);
+                                       else if (!includeFilter && !attrFilter.contains(id))
+                                               effectiveKeys.add(id);
+                               }
+                       } catch (NamingException e) {
+                               throw new UserDirectoryException("Cannot initialise attribute dictionary", e);
+                       }
+               }
+
+               @Override
+               public int size() {
+                       return effectiveKeys.size();
+               }
+
+               @Override
+               public boolean isEmpty() {
+                       return effectiveKeys.size() == 0;
+               }
+
+               @Override
+               public Enumeration<String> keys() {
+                       return Collections.enumeration(effectiveKeys);
+               }
+
+               @Override
+               public Enumeration<Object> elements() {
+                       final Iterator<String> it = effectiveKeys.iterator();
+                       return new Enumeration<Object>() {
+
+                               @Override
+                               public boolean hasMoreElements() {
+                                       return it.hasNext();
+                               }
+
+                               @Override
+                               public Object nextElement() {
+                                       String key = it.next();
+                                       return get(key);
+                               }
+
+                       };
+               }
+
+               @Override
+               public Object get(Object key) {
+                       try {
+                               Attribute attr = getAttributes().get(key.toString());
+                               if (attr == null)
+                                       return null;
+                               Object value = attr.get();
+                               if (value instanceof byte[]) {
+                                       if (key.equals(LdifName.userPassword.name()))
+                                               // TODO other cases (certificates, images)
+                                               return value;
+                                       value = new String((byte[]) value, Charset.forName("UTF-8"));
+                               }
+                               if (attr.size() == 1)
+                                       return value;
+                               if (!attr.getID().equals(LdifName.objectClass.name()))
+                                       return value;
+                               // special case for object class
+                               NamingEnumeration<?> en = attr.getAll();
+                               Set<String> objectClasses = new HashSet<String>();
+                               while (en.hasMore()) {
+                                       String objectClass = en.next().toString();
+                                       objectClasses.add(objectClass);
+                               }
+
+                               if (objectClasses.contains(userAdmin.getUserObjectClass()))
+                                       return userAdmin.getUserObjectClass();
+                               else if (objectClasses.contains(userAdmin.getGroupObjectClass()))
+                                       return userAdmin.getGroupObjectClass();
+                               else
+                                       return value;
+                       } catch (NamingException e) {
+                               throw new UserDirectoryException("Cannot get value for attribute " + key, e);
+                       }
+               }
+
+               @Override
+               public Object put(String key, Object value) {
+                       if (key == null) {
+                               // TODO persist to other sources (like PKCS12)
+                               char[] password = toChars(value);
+                               byte[] hashedPassword = hash(password);
+                               return put(LdifName.userPassword.name(), hashedPassword);
+                       }
+
+                       userAdmin.checkEdit();
+                       if (!isEditing())
+                               startEditing();
+
+                       if (!(value instanceof String || value instanceof byte[]))
+                               throw new IllegalArgumentException("Value must be String or byte[]");
+
+                       if (includeFilter && !attrFilter.contains(key))
+                               throw new IllegalArgumentException("Key " + key + " not included");
+                       else if (!includeFilter && attrFilter.contains(key))
+                               throw new IllegalArgumentException("Key " + key + " excluded");
+
+                       try {
+                               Attribute attribute = getModifiedAttributes().get(key.toString());
+                               attribute = new BasicAttribute(key.toString());
+                               if (value instanceof String && !isAsciiPrintable(((String) value)))
+                                       try {
+                                               attribute.add(((String) value).getBytes("UTF-8"));
+                                       } catch (UnsupportedEncodingException e) {
+                                               throw new UserDirectoryException("Cannot encode " + value, e);
+                                       }
+                               else
+                                       attribute.add(value);
+                               Attribute previousAttribute = getModifiedAttributes().put(attribute);
+                               if (previousAttribute != null)
+                                       return previousAttribute.get();
+                               else
+                                       return null;
+                       } catch (NamingException e) {
+                               throw new UserDirectoryException("Cannot get value for attribute " + key, e);
+                       }
+               }
+
+               @Override
+               public Object remove(Object key) {
+                       userAdmin.checkEdit();
+                       if (!isEditing())
+                               startEditing();
+
+                       if (includeFilter && !attrFilter.contains(key))
+                               throw new IllegalArgumentException("Key " + key + " not included");
+                       else if (!includeFilter && attrFilter.contains(key))
+                               throw new IllegalArgumentException("Key " + key + " excluded");
+
+                       try {
+                               Attribute attr = getModifiedAttributes().remove(key.toString());
+                               if (attr != null)
+                                       return attr.get();
+                               else
+                                       return null;
+                       } catch (NamingException e) {
+                               throw new UserDirectoryException("Cannot remove attribute " + key, e);
+                       }
+               }
+       }
+
+       private static boolean isAsciiPrintable(String str) {
+               if (str == null) {
+                       return false;
+               }
+               int sz = str.length();
+               for (int i = 0; i < sz; i++) {
+                       if (isAsciiPrintable(str.charAt(i)) == false) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+
+       private static boolean isAsciiPrintable(char ch) {
+               return ch >= 32 && ch < 127;
+       }
+
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUserAdmin.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUserAdmin.java
new file mode 100644 (file)
index 0000000..521ae8b
--- /dev/null
@@ -0,0 +1,238 @@
+package org.argeo.osgi.useradmin;
+
+import static org.argeo.osgi.useradmin.LdifName.inetOrgPerson;
+import static org.argeo.osgi.useradmin.LdifName.objectClass;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapName;
+import javax.transaction.TransactionManager;
+
+import org.argeo.util.naming.LdifParser;
+import org.argeo.util.naming.LdifWriter;
+import org.osgi.framework.Filter;
+import org.osgi.service.useradmin.Role;
+
+/**
+ * A user admin based on a LDIF files. Requires a {@link TransactionManager} and
+ * an open transaction for write access.
+ */
+public class LdifUserAdmin extends AbstractUserDirectory {
+       private SortedMap<LdapName, DirectoryUser> users = new TreeMap<LdapName, DirectoryUser>();
+       private SortedMap<LdapName, DirectoryGroup> groups = new TreeMap<LdapName, DirectoryGroup>();
+
+       public LdifUserAdmin(String uri, String baseDn) {
+               this(fromUri(uri, baseDn));
+       }
+
+       public LdifUserAdmin(Dictionary<String, ?> properties) {
+               super(properties);
+       }
+
+       public LdifUserAdmin(InputStream in) {
+               super(new Hashtable<String, Object>());
+               load(in);
+       }
+
+       private static Dictionary<String, Object> fromUri(String uri, String baseDn) {
+               Hashtable<String, Object> res = new Hashtable<String, Object>();
+               res.put(UserAdminConf.uri.name(), uri);
+               res.put(UserAdminConf.baseDn.name(), baseDn);
+               return res;
+       }
+
+       public void init() {
+               try {
+                       if (getUri().getScheme().equals("file")) {
+                               File file = new File(getUri());
+                               if (!file.exists())
+                                       return;
+                       }
+                       load(getUri().toURL().openStream());
+               } catch (Exception e) {
+                       throw new UserDirectoryException("Cannot open URL " + getUri(), e);
+               }
+       }
+
+       public void save() {
+               if (getUri() == null)
+                       throw new UserDirectoryException("Cannot save LDIF user admin: no URI is set");
+               if (isReadOnly())
+                       throw new UserDirectoryException("Cannot save LDIF user admin: " + getUri() + " is read-only");
+               try (FileOutputStream out = new FileOutputStream(new File(getUri()))) {
+                       save(out);
+               } catch (IOException e) {
+                       throw new UserDirectoryException("Cannot save user admin to " + getUri(), e);
+               }
+       }
+
+       public void save(OutputStream out) throws IOException {
+               try {
+                       LdifWriter ldifWriter = new LdifWriter(out);
+                       for (LdapName name : groups.keySet())
+                               ldifWriter.writeEntry(name, groups.get(name).getAttributes());
+                       for (LdapName name : users.keySet())
+                               ldifWriter.writeEntry(name, users.get(name).getAttributes());
+               } finally {
+                       out.close();
+               }
+       }
+
+       protected void load(InputStream in) {
+               try {
+                       users.clear();
+                       groups.clear();
+
+                       LdifParser ldifParser = new LdifParser();
+                       SortedMap<LdapName, Attributes> allEntries = ldifParser.read(in);
+                       for (LdapName key : allEntries.keySet()) {
+                               Attributes attributes = allEntries.get(key);
+                               // check for inconsistency
+                               Set<String> lowerCase = new HashSet<String>();
+                               NamingEnumeration<String> ids = attributes.getIDs();
+                               while (ids.hasMoreElements()) {
+                                       String id = ids.nextElement().toLowerCase();
+                                       if (lowerCase.contains(id))
+                                               throw new UserDirectoryException(key + " has duplicate id " + id);
+                                       lowerCase.add(id);
+                               }
+
+                               // analyse object classes
+                               NamingEnumeration<?> objectClasses = attributes.get(objectClass.name()).getAll();
+                               // System.out.println(key);
+                               objectClasses: while (objectClasses.hasMore()) {
+                                       String objectClass = objectClasses.next().toString();
+                                       // System.out.println(" " + objectClass);
+                                       if (objectClass.equals(inetOrgPerson.name())) {
+                                               users.put(key, new LdifUser(this, key, attributes));
+                                               break objectClasses;
+                                       } else if (objectClass.equals(getGroupObjectClass())) {
+                                               groups.put(key, new LdifGroup(this, key, attributes));
+                                               break objectClasses;
+                                       }
+                               }
+                       }
+               } catch (Exception e) {
+                       throw new UserDirectoryException("Cannot load user admin service from LDIF", e);
+               }
+       }
+
+       public void destroy() {
+               if (users == null || groups == null)
+                       throw new UserDirectoryException("User directory " + getBaseDn() + " is already destroyed");
+               users.clear();
+               users = null;
+               groups.clear();
+               groups = null;
+       }
+
+       protected DirectoryUser daoGetRole(LdapName key) {
+               if (groups.containsKey(key))
+                       return groups.get(key);
+               if (users.containsKey(key))
+                       return users.get(key);
+               return null;
+       }
+
+       protected Boolean daoHasRole(LdapName dn) {
+               return users.containsKey(dn) || groups.containsKey(dn);
+       }
+
+       @SuppressWarnings("unchecked")
+       protected List<DirectoryUser> doGetRoles(Filter f) {
+               ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
+               if (f == null) {
+                       res.addAll(users.values());
+                       res.addAll(groups.values());
+               } else {
+                       for (DirectoryUser user : users.values()) {
+                               // System.out.println("\n" + user.getName());
+                               // Dictionary<String, Object> props = user.getProperties();
+                               // for (Enumeration<String> keys = props.keys(); keys
+                               // .hasMoreElements();) {
+                               // String key = keys.nextElement();
+                               // System.out.println(" " + key + "=" + props.get(key));
+                               // }
+                               if (f.match(user.getProperties()))
+                                       res.add(user);
+                       }
+                       for (DirectoryUser group : groups.values())
+                               if (f.match(group.getProperties()))
+                                       res.add(group);
+               }
+               return res;
+       }
+
+       @Override
+       protected List<LdapName> getDirectGroups(LdapName dn) {
+               List<LdapName> directGroups = new ArrayList<LdapName>();
+               for (LdapName name : groups.keySet()) {
+                       DirectoryGroup group = groups.get(name);
+                       if (group.getMemberNames().contains(dn))
+                               directGroups.add(group.getDn());
+               }
+               return directGroups;
+       }
+
+       @Override
+       protected void prepare(UserDirectoryWorkingCopy wc) {
+               // delete
+               for (LdapName dn : wc.getDeletedUsers().keySet()) {
+                       if (users.containsKey(dn))
+                               users.remove(dn);
+                       else if (groups.containsKey(dn))
+                               groups.remove(dn);
+                       else
+                               throw new UserDirectoryException("User to delete not found " + dn);
+               }
+               // add
+               for (LdapName dn : wc.getNewUsers().keySet()) {
+                       DirectoryUser user = wc.getNewUsers().get(dn);
+                       if (users.containsKey(dn) || groups.containsKey(dn))
+                               throw new UserDirectoryException("User to create found " + dn);
+                       else if (Role.USER == user.getType())
+                               users.put(dn, user);
+                       else if (Role.GROUP == user.getType())
+                               groups.put(dn, (DirectoryGroup) user);
+                       else
+                               throw new UserDirectoryException("Unsupported role type " + user.getType() + " for new user " + dn);
+               }
+               // modify
+               for (LdapName dn : wc.getModifiedUsers().keySet()) {
+                       Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
+                       DirectoryUser user;
+                       if (users.containsKey(dn))
+                               user = users.get(dn);
+                       else if (groups.containsKey(dn))
+                               user = groups.get(dn);
+                       else
+                               throw new UserDirectoryException("User to modify no found " + dn);
+                       user.publishAttributes(modifiedAttrs);
+               }
+       }
+
+       @Override
+       protected void commit(UserDirectoryWorkingCopy wc) {
+               save();
+       }
+
+       @Override
+       protected void rollback(UserDirectoryWorkingCopy wc) {
+               init();
+       }
+
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java
new file mode 100644 (file)
index 0000000..316941e
--- /dev/null
@@ -0,0 +1,193 @@
+package org.argeo.osgi.useradmin;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLDecoder;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.Context;
+
+import org.osgi.framework.Constants;
+
+/** Properties used to configure user admins. */
+public enum UserAdminConf {
+       /** Base DN (cannot be configured externally) */
+       baseDn("dc=example,dc=com"),
+
+       /** URI of the underlying resource (cannot be configured externally) */
+       uri("ldap://localhost:10389"),
+
+       /** User objectClass */
+       userObjectClass("inetOrgPerson"),
+
+       /** Relative base DN for users */
+       userBase("ou=People"),
+
+       /** Groups objectClass */
+       groupObjectClass("groupOfNames"),
+
+       /** Relative base DN for users */
+       groupBase("ou=Groups"),
+
+       /** Read-only source */
+       readOnly(null);
+
+       public final static String FACTORY_PID = "org.argeo.osgi.useradmin.config";
+
+       /** The default value. */
+       private Object def;
+
+       UserAdminConf(Object def) {
+               this.def = def;
+       }
+
+       public Object getDefault() {
+               return def;
+       }
+
+       /**
+        * For use as Java property.
+        * 
+        * @deprecated use {@link #name()} instead
+        */
+       @Deprecated
+       public String property() {
+               return name();
+       }
+
+       public String getValue(Dictionary<String, ?> properties) {
+               Object res = getRawValue(properties);
+               if (res == null)
+                       return null;
+               return res.toString();
+       }
+
+       @SuppressWarnings("unchecked")
+       public <T> T getRawValue(Dictionary<String, ?> properties) {
+               Object res = properties.get(name());
+               if (res == null)
+                       res = getDefault();
+               return (T) res;
+       }
+
+       /** @deprecated use {@link #valueOf(String)} instead */
+       @Deprecated
+       public static UserAdminConf local(String property) {
+               return UserAdminConf.valueOf(property);
+       }
+
+       /** Hides host and credentials. */
+       public static URI propertiesAsUri(Dictionary<String, ?> properties) {
+               StringBuilder query = new StringBuilder();
+
+               boolean first = true;
+               for (Enumeration<String> keys = properties.keys(); keys.hasMoreElements();) {
+                       String key = keys.nextElement();
+                       // TODO clarify which keys are relevant (list only the enum?)
+                       if (!key.equals("service.factoryPid") && !key.equals("cn") && !key.equals("dn")
+                                       && !key.equals(Constants.SERVICE_PID) && !key.startsWith("java") && !key.equals(baseDn.name())
+                                       && !key.equals(uri.name())) {
+                               if (first)
+                                       first = false;
+                               else
+                                       query.append('&');
+                               query.append(valueOf(key).name());
+                               query.append('=').append(properties.get(key).toString());
+                       }
+               }
+
+               String bDn = (String) properties.get(baseDn.name());
+               try {
+                       return new URI(null, null, bDn != null ? '/' + bDn : null, query.length() != 0 ? query.toString() : null,
+                                       null);
+               } catch (URISyntaxException e) {
+                       throw new UserDirectoryException("Cannot create URI from properties", e);
+               }
+       }
+
+       public static Dictionary<String, Object> uriAsProperties(String uriStr) {
+               try {
+                       Hashtable<String, Object> res = new Hashtable<String, Object>();
+                       URI u = new URI(uriStr);
+                       String scheme = u.getScheme();
+                       String path = u.getPath();
+                       String bDn = path.substring(path.lastIndexOf('/') + 1, path.length());
+                       if (bDn.endsWith(".ldif"))
+                               bDn = bDn.substring(0, bDn.length() - ".ldif".length());
+
+                       String principal = null;
+                       String credentials = null;
+                       if (scheme != null)
+                               if (scheme.equals("ldap") || scheme.equals("ldaps")) {
+                                       // TODO additional checks
+                                       String[] userInfo = u.getUserInfo().split(":");
+                                       principal = userInfo.length > 0 ? userInfo[0] : null;
+                                       credentials = userInfo.length > 1 ? userInfo[1] : null;
+                               } else if (scheme.equals("file")) {
+                               } else
+                                       throw new UserDirectoryException("Unsupported scheme " + scheme);
+                       Map<String, List<String>> query = splitQuery(u.getQuery());
+                       for (String key : query.keySet()) {
+                               UserAdminConf ldapProp = UserAdminConf.valueOf(key);
+                               List<String> values = query.get(key);
+                               if (values.size() == 1) {
+                                       res.put(ldapProp.name(), values.get(0));
+                               } else {
+                                       throw new UserDirectoryException("Only single values are supported");
+                               }
+                       }
+                       res.put(baseDn.name(), bDn);
+                       if (principal != null)
+                               res.put(Context.SECURITY_PRINCIPAL, principal);
+                       if (credentials != null)
+                               res.put(Context.SECURITY_CREDENTIALS, credentials);
+                       if (scheme != null) {
+                               URI bareUri = new URI(scheme, null, u.getHost(), u.getPort(),
+                                               scheme.equals("file") ? u.getPath() : null, null, null);
+                               res.put(uri.name(), bareUri.toString());
+                       }
+                       return res;
+               } catch (Exception e) {
+                       throw new UserDirectoryException("Cannot convert " + uri + " to properties", e);
+               }
+       }
+
+       private static Map<String, List<String>> splitQuery(String query) throws UnsupportedEncodingException {
+               final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
+               if (query == null)
+                       return query_pairs;
+               final String[] pairs = query.split("&");
+               for (String pair : pairs) {
+                       final int idx = pair.indexOf("=");
+                       final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
+                       if (!query_pairs.containsKey(key)) {
+                               query_pairs.put(key, new LinkedList<String>());
+                       }
+                       final String value = idx > 0 && pair.length() > idx + 1
+                                       ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
+                       query_pairs.get(key).add(value);
+               }
+               return query_pairs;
+       }
+
+       public static void main(String[] args) {
+               Dictionary<String, ?> props = uriAsProperties("ldap://" + "uid=admin,ou=system:secret@localhost:10389"
+                               + "/dc=example,dc=com" + "?readOnly=false&userObjectClass=person");
+               System.out.println(props);
+               System.out.println(propertiesAsUri(props));
+
+               System.out.println(uriAsProperties("file://some/dir/dc=example,dc=com.ldif"));
+
+               props = uriAsProperties(
+                               "/dc=example,dc=com.ldif?readOnly=true" + "&userBase=ou=CoWorkers,ou=People&groupBase=ou=Roles");
+               System.out.println(props);
+               System.out.println(propertiesAsUri(props));
+       }
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectory.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectory.java
new file mode 100644 (file)
index 0000000..e5de738
--- /dev/null
@@ -0,0 +1,23 @@
+package org.argeo.osgi.useradmin;
+
+import javax.naming.ldap.LdapName;
+import javax.transaction.xa.XAResource;
+
+/** Information about a user directory. */
+public interface UserDirectory {
+       /** The base DN of all entries in this user directory */
+       public LdapName getBaseDn();
+
+       /** The related {@link XAResource} */
+       public XAResource getXaResource();
+
+       public boolean isReadOnly();
+
+       public String getUserObjectClass();
+
+       public String getUserBase();
+
+       public String getGroupObjectClass();
+
+       public String getGroupBase();
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectoryException.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectoryException.java
new file mode 100644 (file)
index 0000000..613d0fd
--- /dev/null
@@ -0,0 +1,19 @@
+package org.argeo.osgi.useradmin;
+
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * Exceptions related to Argeo's implementation of OSGi {@link UserAdmin}
+ * service.
+ */
+public class UserDirectoryException extends RuntimeException {
+       private static final long serialVersionUID = 1419352360062048603L;
+
+       public UserDirectoryException(String message) {
+               super(message);
+       }
+
+       public UserDirectoryException(String message, Throwable e) {
+               super(message, e);
+       }
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java
new file mode 100644 (file)
index 0000000..0e25bdf
--- /dev/null
@@ -0,0 +1,58 @@
+package org.argeo.osgi.useradmin;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapName;
+import javax.transaction.xa.XAResource;
+
+/** {@link XAResource} for a user directory being edited. */
+class UserDirectoryWorkingCopy {
+       // private final static Log log = LogFactory
+       // .getLog(UserDirectoryWorkingCopy.class);
+
+       private Map<LdapName, DirectoryUser> newUsers = new HashMap<LdapName, DirectoryUser>();
+       private Map<LdapName, Attributes> modifiedUsers = new HashMap<LdapName, Attributes>();
+       private Map<LdapName, DirectoryUser> deletedUsers = new HashMap<LdapName, DirectoryUser>();
+
+       void cleanUp() {
+               // clean collections
+               newUsers.clear();
+               newUsers = null;
+               modifiedUsers.clear();
+               modifiedUsers = null;
+               deletedUsers.clear();
+               deletedUsers = null;
+       }
+
+       public boolean noModifications() {
+               return newUsers.size() == 0 && modifiedUsers.size() == 0
+                               && deletedUsers.size() == 0;
+       }
+
+       public Attributes getAttributes(LdapName dn) {
+               if (modifiedUsers.containsKey(dn))
+                       return modifiedUsers.get(dn);
+               return null;
+       }
+
+       public void startEditing(DirectoryUser user) {
+               LdapName dn = user.getDn();
+               if (modifiedUsers.containsKey(dn))
+                       throw new UserDirectoryException("Already editing " + dn);
+               modifiedUsers.put(dn, (Attributes) user.getAttributes().clone());
+       }
+
+       public Map<LdapName, DirectoryUser> getNewUsers() {
+               return newUsers;
+       }
+
+       public Map<LdapName, DirectoryUser> getDeletedUsers() {
+               return deletedUsers;
+       }
+
+       public Map<LdapName, Attributes> getModifiedUsers() {
+               return modifiedUsers;
+       }
+}
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/WcXaResource.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/WcXaResource.java
new file mode 100644 (file)
index 0000000..a6048fd
--- /dev/null
@@ -0,0 +1,143 @@
+package org.argeo.osgi.useradmin;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/** {@link XAResource} for a user directory being edited. */
+class WcXaResource implements XAResource {
+       private final static Log log = LogFactory.getLog(WcXaResource.class);
+
+       private final AbstractUserDirectory userDirectory;
+
+       private Map<Xid, UserDirectoryWorkingCopy> workingCopies = new HashMap<Xid, UserDirectoryWorkingCopy>();
+       private Xid editingXid = null;
+       private int transactionTimeout = 0;
+
+       public WcXaResource(AbstractUserDirectory userDirectory) {
+               this.userDirectory = userDirectory;
+       }
+
+       @Override
+       public synchronized void start(Xid xid, int flags) throws XAException {
+               if (editingXid != null)
+                       throw new UserDirectoryException("Already editing " + editingXid);
+               UserDirectoryWorkingCopy wc = workingCopies.put(xid,
+                               new UserDirectoryWorkingCopy());
+               if (wc != null)
+                       throw new UserDirectoryException(
+                                       "There is already a working copy for " + xid);
+               this.editingXid = xid;
+       }
+
+       @Override
+       public void end(Xid xid, int flags) throws XAException {
+               checkXid(xid);
+       }
+
+       private UserDirectoryWorkingCopy wc(Xid xid) {
+               return workingCopies.get(xid);
+       }
+
+       synchronized UserDirectoryWorkingCopy wc() {
+               if (editingXid == null)
+                       return null;
+               UserDirectoryWorkingCopy wc = workingCopies.get(editingXid);
+               if (wc == null)
+                       throw new UserDirectoryException("No working copy found for "
+                                       + editingXid);
+               return wc;
+       }
+
+       private synchronized void cleanUp(Xid xid) {
+               wc(xid).cleanUp();
+               workingCopies.remove(xid);
+               editingXid = null;
+       }
+
+       @Override
+       public int prepare(Xid xid) throws XAException {
+               checkXid(xid);
+               UserDirectoryWorkingCopy wc = wc(xid);
+               if (wc.noModifications())
+                       return XA_RDONLY;
+               try {
+                       userDirectory.prepare(wc);
+               } catch (Exception e) {
+                       log.error("Cannot prepare " + xid, e);
+                       throw new XAException(XAException.XAER_RMERR);
+               }
+               return XA_OK;
+       }
+
+       @Override
+       public void commit(Xid xid, boolean onePhase) throws XAException {
+               try {
+                       checkXid(xid);
+                       UserDirectoryWorkingCopy wc = wc(xid);
+                       if (wc.noModifications())
+                               return;
+                       if (onePhase)
+                               userDirectory.prepare(wc);
+                       userDirectory.commit(wc);
+               } catch (Exception e) {
+                       log.error("Cannot commit " + xid, e);
+                       throw new XAException(XAException.XAER_RMERR);
+               } finally {
+                       cleanUp(xid);
+               }
+       }
+
+       @Override
+       public void rollback(Xid xid) throws XAException {
+               try {
+                       checkXid(xid);
+                       userDirectory.rollback(wc(xid));
+               } catch (Exception e) {
+                       log.error("Cannot rollback " + xid, e);
+                       throw new XAException(XAException.XAER_RMERR);
+               } finally {
+                       cleanUp(xid);
+               }
+       }
+
+       @Override
+       public void forget(Xid xid) throws XAException {
+               throw new UnsupportedOperationException();
+       }
+
+       @Override
+       public boolean isSameRM(XAResource xares) throws XAException {
+               return xares == this;
+       }
+
+       @Override
+       public Xid[] recover(int flag) throws XAException {
+               return new Xid[0];
+       }
+
+       @Override
+       public int getTransactionTimeout() throws XAException {
+               return transactionTimeout;
+       }
+
+       @Override
+       public boolean setTransactionTimeout(int seconds) throws XAException {
+               transactionTimeout = seconds;
+               return true;
+       }
+
+       private void checkXid(Xid xid) throws XAException {
+               if (xid == null)
+                       throw new XAException(XAException.XAER_OUTSIDE);
+               if (!xid.equals(xid))
+                       throw new XAException(XAException.XAER_NOTA);
+       }
+
+}
diff --git a/org.argeo.enterprise/src/org/argeo/util/naming/AttributesDictionary.java b/org.argeo.enterprise/src/org/argeo/util/naming/AttributesDictionary.java
new file mode 100644 (file)
index 0000000..c211e8e
--- /dev/null
@@ -0,0 +1,171 @@
+package org.argeo.util.naming;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+
+public class AttributesDictionary extends Dictionary<String, Object> {
+       private final Attributes attributes;
+
+       /** The provided attributes is wrapped, not copied. */
+       public AttributesDictionary(Attributes attributes) {
+               if (attributes == null)
+                       throw new IllegalArgumentException("Attributes cannot be null");
+               this.attributes = attributes;
+       }
+
+       @Override
+       public int size() {
+               return attributes.size();
+       }
+
+       @Override
+       public boolean isEmpty() {
+               return attributes.size() == 0;
+       }
+
+       @Override
+       public Enumeration<String> keys() {
+               NamingEnumeration<String> namingEnumeration = attributes.getIDs();
+               return new Enumeration<String>() {
+
+                       @Override
+                       public boolean hasMoreElements() {
+                               return namingEnumeration.hasMoreElements();
+                       }
+
+                       @Override
+                       public String nextElement() {
+                               return namingEnumeration.nextElement();
+                       }
+
+               };
+       }
+
+       @Override
+       public Enumeration<Object> elements() {
+               NamingEnumeration<String> namingEnumeration = attributes.getIDs();
+               return new Enumeration<Object>() {
+
+                       @Override
+                       public boolean hasMoreElements() {
+                               return namingEnumeration.hasMoreElements();
+                       }
+
+                       @Override
+                       public Object nextElement() {
+                               String key = namingEnumeration.nextElement();
+                               return get(key);
+                       }
+
+               };
+       }
+
+       @Override
+       /** @returns a <code>String</code> or <code>String[]</code> */
+       public Object get(Object key) {
+               try {
+                       if (key == null)
+                               throw new IllegalArgumentException("Key cannot be null");
+                       Attribute attr = attributes.get(key.toString());
+                       if (attr == null)
+                               return null;
+                       if (attr.size() == 0)
+                               throw new IllegalStateException("There must be at least one value");
+                       else if (attr.size() == 1) {
+                               return attr.get().toString();
+                       } else {// multiple
+                               String[] res = new String[attr.size()];
+                               for (int i = 0; i < attr.size(); i++) {
+                                       Object value = attr.get();
+                                       if (value == null)
+                                               throw new RuntimeException("Values cannot be null");
+                                       res[i] = attr.get(i).toString();
+                               }
+                               return res;
+                       }
+               } catch (NamingException e) {
+                       throw new RuntimeException("Cannot get value for " + key, e);
+               }
+       }
+
+       @Override
+       public Object put(String key, Object value) {
+               if (key == null)
+                       throw new IllegalArgumentException("Key cannot be null");
+               if (value == null)
+                       throw new IllegalArgumentException("Value cannot be null");
+
+               Object oldValue = get(key);
+               Attribute attr = attributes.get(key);
+               if (attr == null) {
+                       attr = new BasicAttribute(key);
+                       attributes.put(attr);
+               }
+
+               if (value instanceof String[]) {
+                       String[] values = (String[]) value;
+                       // clean additional values
+                       for (int i = values.length; i < attr.size(); i++)
+                               attr.remove(i);
+                       // set values
+                       for (int i = 0; i < values.length; i++) {
+                               attr.set(i, values[i]);
+                       }
+               } else {
+                       if (attr.size() > 1)
+                               throw new IllegalArgumentException("Attribute " + key + " is multi-valued");
+                       if (attr.size() == 1) {
+                               try {
+                                       if (!attr.get(0).equals(value))
+                                               attr.set(0, value.toString());
+                               } catch (NamingException e) {
+                                       throw new RuntimeException("Cannot check existing value", e);
+                               }
+                       } else {
+                               attr.add(value.toString());
+                       }
+               }
+               return oldValue;
+       }
+
+       @Override
+       public Object remove(Object key) {
+               if (key == null)
+                       throw new IllegalArgumentException("Key cannot be null");
+               Object oldValue = get(key);
+               if (oldValue == null)
+                       return null;
+               return attributes.remove(key.toString());
+       }
+
+       /**
+        * Copy the <b>content</b> of an {@link javax.naming.Attributes} to the
+        * provided {@link Dictionary}.
+        */
+       public static void copy(Attributes attributes, Dictionary<String, Object> dictionary) {
+               AttributesDictionary ad = new AttributesDictionary(attributes);
+               Enumeration<String> keys = ad.keys();
+               while (keys.hasMoreElements()) {
+                       String key = keys.nextElement();
+                       dictionary.put(key, ad.get(key));
+               }
+       }
+
+       /**
+        * Copy a {@link Dictionary} into an {@link javax.naming.Attributes}.
+        */
+       public static void copy(Dictionary<String, Object> dictionary, Attributes attributes) {
+               AttributesDictionary ad = new AttributesDictionary(attributes);
+               Enumeration<String> keys = dictionary.keys();
+               while (keys.hasMoreElements()) {
+                       String key = keys.nextElement();
+                       ad.put(key, dictionary.get(key));
+               }
+       }
+}
diff --git a/org.argeo.enterprise/src/org/argeo/util/naming/LdifParser.java b/org.argeo.enterprise/src/org/argeo/util/naming/LdifParser.java
new file mode 100644 (file)
index 0000000..ec73e8a
--- /dev/null
@@ -0,0 +1,145 @@
+package org.argeo.util.naming;
+
+import static org.argeo.osgi.useradmin.LdifName.dn;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.osgi.useradmin.UserDirectoryException;
+
+/** Basic LDIF parser. */
+public class LdifParser {
+       private final static Log log = LogFactory.getLog(LdifParser.class);
+
+       protected Attributes addAttributes(SortedMap<LdapName, Attributes> res, int lineNumber, LdapName currentDn,
+                       Attributes currentAttributes) {
+               try {
+                       Rdn nameRdn = currentDn.getRdn(currentDn.size() - 1);
+                       Attribute nameAttr = currentAttributes.get(nameRdn.getType());
+                       if (nameAttr == null)
+                               currentAttributes.put(nameRdn.getType(), nameRdn.getValue());
+                       else if (!nameAttr.get().equals(nameRdn.getValue()))
+                               throw new UserDirectoryException(
+                                               "Attribute " + nameAttr.getID() + "=" + nameAttr.get() + " not consistent with DN " + currentDn
+                                                               + " (shortly before line " + lineNumber + " in LDIF file)");
+                       Attributes previous = res.put(currentDn, currentAttributes);
+                       if (log.isTraceEnabled())
+                               log.trace("Added " + currentDn);
+                       return previous;
+               } catch (NamingException e) {
+                       throw new UserDirectoryException("Cannot add " + currentDn, e);
+               }
+       }
+
+       public SortedMap<LdapName, Attributes> read(InputStream in) throws IOException {
+               SortedMap<LdapName, Attributes> res = new TreeMap<LdapName, Attributes>();
+               try {
+                       List<String> lines = new ArrayList<>();
+                       try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
+                               String line;
+                               while ((line = br.readLine()) != null) {
+                                       lines.add(line);
+                               }
+                       }
+                       if (lines.size() == 0)
+                               return res;
+                       // add an empty new line since the last line is not checked
+                       if (!lines.get(lines.size() - 1).equals(""))
+                               lines.add("");
+
+                       LdapName currentDn = null;
+                       Attributes currentAttributes = null;
+                       StringBuilder currentEntry = new StringBuilder();
+
+                       readLines: for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
+                               String line = lines.get(lineNumber);
+                               boolean isLastLine = false;
+                               if (lineNumber == lines.size() - 1)
+                                       isLastLine = true;
+                               if (line.startsWith(" ")) {
+                                       currentEntry.append(line.substring(1));
+                                       if (!isLastLine)
+                                               continue readLines;
+                               }
+
+                               if (currentEntry.length() != 0 || isLastLine) {
+                                       // read previous attribute
+                                       StringBuilder attrId = new StringBuilder(8);
+                                       boolean isBase64 = false;
+                                       readAttrId: for (int i = 0; i < currentEntry.length(); i++) {
+                                               char c = currentEntry.charAt(i);
+                                               if (c == ':') {
+                                                       if (i + 1 < currentEntry.length() && currentEntry.charAt(i + 1) == ':')
+                                                               isBase64 = true;
+                                                       currentEntry.delete(0, i + (isBase64 ? 2 : 1));
+                                                       break readAttrId;
+                                               } else {
+                                                       attrId.append(c);
+                                               }
+                                       }
+
+                                       String attributeId = attrId.toString();
+                                       String cleanValueStr = currentEntry.toString().trim();
+                                       Object attributeValue = isBase64 ? Base64.getDecoder().decode(cleanValueStr) : cleanValueStr;
+
+                                       // manage DN attributes
+                                       if (attributeId.equals(dn.name()) || isLastLine) {
+                                               if (currentDn != null) {
+                                                       //
+                                                       // ADD
+                                                       //
+                                                       Attributes previous = addAttributes(res, lineNumber, currentDn, currentAttributes);
+                                                       if (previous != null) {
+                                                               log.warn("There was already an entry with DN " + currentDn
+                                                                               + ", which has been discarded by a subsequent one.");
+                                                       }
+                                               }
+
+                                               if (attributeId.equals(dn.name()))
+                                                       try {
+                                                               currentDn = new LdapName(attributeValue.toString());
+                                                               currentAttributes = new BasicAttributes(true);
+                                                       } catch (InvalidNameException e) {
+                                                               log.error(attributeValue + " not a valid DN, skipping the entry.");
+                                                               currentDn = null;
+                                                               currentAttributes = null;
+                                                       }
+                                       }
+
+                                       // store attribute
+                                       if (currentAttributes != null) {
+                                               Attribute attribute = currentAttributes.get(attributeId);
+                                               if (attribute == null) {
+                                                       attribute = new BasicAttribute(attributeId);
+                                                       currentAttributes.put(attribute);
+                                               }
+                                               attribute.add(attributeValue);
+                                       }
+                                       currentEntry = new StringBuilder();
+                               }
+                               currentEntry.append(line);
+                       }
+               } finally {
+                       in.close();
+               }
+               return res;
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.enterprise/src/org/argeo/util/naming/LdifWriter.java b/org.argeo.enterprise/src/org/argeo/util/naming/LdifWriter.java
new file mode 100644 (file)
index 0000000..37d90b4
--- /dev/null
@@ -0,0 +1,77 @@
+package org.argeo.util.naming;
+
+import static org.argeo.osgi.useradmin.LdifName.dn;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Base64;
+import java.util.Map;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
+import org.argeo.osgi.useradmin.UserDirectoryException;
+
+/** Basic LDIF writer */
+public class LdifWriter {
+       private final Writer writer;
+
+       /** Writer must be closed by caller */
+       public LdifWriter(Writer writer) {
+               this.writer = writer;
+       }
+
+       /** Stream must be closed by caller */
+       public LdifWriter(OutputStream out) {
+               this(new OutputStreamWriter(out));
+       }
+
+       public void writeEntry(LdapName name, Attributes attributes) throws IOException {
+               try {
+                       // check consistency
+                       Rdn nameRdn = name.getRdn(name.size() - 1);
+                       Attribute nameAttr = attributes.get(nameRdn.getType());
+                       if (!nameAttr.get().equals(nameRdn.getValue()))
+                               throw new UserDirectoryException(
+                                               "Attribute " + nameAttr.getID() + "=" + nameAttr.get() + " not consistent with DN " + name);
+
+                       writer.append(dn.name() + ":").append(name.toString()).append('\n');
+                       Attribute objectClassAttr = attributes.get("objectClass");
+                       if (objectClassAttr != null)
+                               writeAttribute(objectClassAttr);
+                       for (NamingEnumeration<? extends Attribute> attrs = attributes.getAll(); attrs.hasMore();) {
+                               Attribute attribute = attrs.next();
+                               if (attribute.getID().equals(dn.name()) || attribute.getID().equals("objectClass"))
+                                       continue;// skip DN attribute
+                               writeAttribute(attribute);
+                       }
+                       writer.append('\n');
+                       writer.flush();
+               } catch (NamingException e) {
+                       throw new UserDirectoryException("Cannot write LDIF", e);
+               }
+       }
+
+       public void write(Map<LdapName, Attributes> entries) throws IOException {
+               for (LdapName dn : entries.keySet())
+                       writeEntry(dn, entries.get(dn));
+       }
+
+       protected void writeAttribute(Attribute attribute) throws NamingException, IOException {
+               for (NamingEnumeration<?> attrValues = attribute.getAll(); attrValues.hasMore();) {
+                       Object value = attrValues.next();
+                       if (value instanceof byte[]) {
+                               String encoded = Base64.getEncoder().encodeToString((byte[]) value);
+                               writer.append(attribute.getID()).append("::").append(encoded).append('\n');
+                       } else {
+                               writer.append(attribute.getID()).append(':').append(value.toString()).append('\n');
+                       }
+               }
+       }
+}
diff --git a/org.argeo.ext.jackrabbit/.classpath b/org.argeo.ext.jackrabbit/.classpath
new file mode 100644 (file)
index 0000000..a8a298a
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="src" path="ext/test"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.argeo.ext.jackrabbit/.project b/org.argeo.ext.jackrabbit/.project
new file mode 100644 (file)
index 0000000..fde35cc
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.ext.jackrabbit</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/org.argeo.ext.jackrabbit/bnd.bnd b/org.argeo.ext.jackrabbit/bnd.bnd
new file mode 100644 (file)
index 0000000..6bab2aa
--- /dev/null
@@ -0,0 +1,3 @@
+Fragment-Host: org.apache.jackrabbit.core
+Import-Package: org.springframework.core,\
+*
diff --git a/org.argeo.ext.jackrabbit/build.properties b/org.argeo.ext.jackrabbit/build.properties
new file mode 100644 (file)
index 0000000..8cc3392
--- /dev/null
@@ -0,0 +1,23 @@
+source.. = src/,\
+           ext/test/
+
+additional.bundles = org.junit,\
+                     org.apache.jackrabbit.core,\
+                     javax.jcr,\
+                     org.apache.jackrabbit.api,\
+                     org.apache.jackrabbit.data,\
+                     org.apache.jackrabbit.jcr.commons,\
+                     org.apache.jackrabbit.spi,\
+                     org.apache.jackrabbit.spi.commons,\
+                     org.slf4j.api,\
+                     org.slf4j.commons.logging,\
+                     org.slf4j.log4j12,\
+                     org.apache.log4j,\
+                     org.apache.commons.collections,\
+                     EDU.oswego.cs.dl.util.concurrent,\
+                     org.apache.lucene,\
+                     org.apache.tika.core,\
+                     org.apache.commons.dbcp,\
+                     org.apache.commons.pool,\
+                     org.argeo.jcr
+
diff --git a/org.argeo.ext.jackrabbit/ext/test/log4j.properties b/org.argeo.ext.jackrabbit/ext/test/log4j.properties
new file mode 100644 (file)
index 0000000..b4edd7c
--- /dev/null
@@ -0,0 +1,17 @@
+log4j.rootLogger=WARN, console
+
+## Levels
+log4j.logger.org.argeo=DEBUG
+log4j.logger.org.apache.jackrabbit=OFF
+log4j.logger.org.apache.jackrabbit.core.security=DEBUG
+log4j.logger.org.apache.jackrabbit.core.DefaultSecurityManager=DEBUG
+
+## Appenders
+# console is set to be a ConsoleAppender.
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+
+# console uses PatternLayout.
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+#log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n
+#log4j.appender.console.layout.ConversionPattern=%m%n
+log4j.appender.console.layout.ConversionPattern=%d{ABSOLUTE} %m (%F:%L) [%t] %p %n
diff --git a/org.argeo.ext.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java b/org.argeo.ext.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java
new file mode 100644 (file)
index 0000000..47afff9
--- /dev/null
@@ -0,0 +1,52 @@
+package org.argeo.security.jackrabbit;
+
+import javax.jcr.Repository;
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase;
+
+public class JackrabbitAuthTest extends AbstractJackrabbitTestCase {
+       private final Log log = LogFactory.getLog(JackrabbitAuthTest.class);
+
+       public void testLogin() throws Exception {
+               Session session = session();
+               log.debug(session.getUserID());
+               assertEquals("admin", session.getUserID());
+               // Subject subject = new Subject();
+               // LoginContext loginContext = new LoginContext("SYSTEM", subject);
+               // loginContext.login();
+               // Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
+               //
+               // @Override
+               // public Void run() throws Exception {
+               // Repository repository = getRepository();
+               // Session session = repository.login();
+               // log.debug(session.getUserID());
+               // return null;
+               // }
+               // });
+       }
+
+       @Override
+       protected String getLoginContext() {
+               return LOGIN_CONTEXT_TEST_SYSTEM;
+       }
+
+       @Override
+       protected Repository createRepository() throws Exception {
+               return super.createRepository();
+       }
+
+       @Override
+       protected void clearRepository(Repository repository) throws Exception {
+               // System.setProperty("java.security.auth.login.config", "");
+       }
+
+       @Override
+       protected String getRepositoryConfigResource() {
+               return "/org/argeo/security/jackrabbit/repository-memory-test.xml";
+       }
+
+}
diff --git a/org.argeo.ext.jackrabbit/ext/test/org/argeo/security/jackrabbit/repository-memory-test.xml b/org.argeo.ext.jackrabbit/ext/test/org/argeo/security/jackrabbit/repository-memory-test.xml
new file mode 100644 (file)
index 0000000..e285555
--- /dev/null
@@ -0,0 +1,65 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (C) 2007-2012 Argeo GmbH
+
+    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.
+
+-->
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
+                            "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
+<Repository>
+       <!-- File system and datastore -->
+       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+
+       <!-- Workspace templates -->
+       <Workspaces rootPath="${rep.home}/workspaces"
+               defaultWorkspace="main" configRootPath="/workspaces" />
+       <Workspace name="${wsp.name}">
+               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+                       <param name="blobFSBlockSize" value="1" />
+               </PersistenceManager>
+               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+                       <param name="path" value="${rep.home}/repository/index" />
+                       <param name="directoryManagerClass"
+                               value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
+                       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+               </SearchIndex>
+       </Workspace>
+
+       <!-- Versioning -->
+       <Versioning rootPath="${rep.home}/version">
+               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+                       <param name="blobFSBlockSize" value="1" />
+               </PersistenceManager>
+       </Versioning>
+
+       <!-- Indexing -->
+       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+               <param name="path" value="${rep.home}/repository/index" />
+               <param name="directoryManagerClass"
+                       value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
+               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security"/>
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager"/>
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/org.argeo.ext.jackrabbit/pom.xml b/org.argeo.ext.jackrabbit/pom.xml
new file mode 100644 (file)
index 0000000..15d5e61
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <artifactId>argeo-commons</artifactId>
+               <version>2.1.46-SNAPSHOT</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.ext.jackrabbit</artifactId>
+       <name>Extensions Apache Jackrabbit</name>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.node.api</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
+
+               <!-- TESTING -->
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.jcr</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+                       <scope>test</scope>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessControlProvider.java b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessControlProvider.java
new file mode 100644 (file)
index 0000000..cd0cf86
--- /dev/null
@@ -0,0 +1,22 @@
+package org.argeo.security.jackrabbit;
+
+import java.util.Map;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.core.security.authorization.acl.ACLProvider;
+
+/** Argeo specific access control provider */
+public class ArgeoAccessControlProvider extends ACLProvider {
+
+       @SuppressWarnings({ "rawtypes", "unchecked" })
+       @Override
+       public void init(Session systemSession, Map configuration)
+                       throws RepositoryException {
+               if (!configuration.containsKey(PARAM_ALLOW_UNKNOWN_PRINCIPALS))
+                       configuration.put(PARAM_ALLOW_UNKNOWN_PRINCIPALS, "true");
+               super.init(systemSession, configuration);
+       }
+
+}
diff --git a/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessManager.java b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessManager.java
new file mode 100644 (file)
index 0000000..52ea3c9
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.jackrabbit;
+
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.security.Privilege;
+
+import org.apache.jackrabbit.core.id.ItemId;
+import org.apache.jackrabbit.core.security.DefaultAccessManager;
+import org.apache.jackrabbit.spi.Path;
+
+/**
+ * Intermediary class in order to have a consistent naming in config files. Does
+ * nothing for the time being, but may in the future.
+ */
+public class ArgeoAccessManager extends DefaultAccessManager {
+
+       @Override
+       public boolean canRead(Path itemPath, ItemId itemId)
+                       throws RepositoryException {
+               return super.canRead(itemPath, itemId);
+       }
+
+       @Override
+       public Privilege[] getPrivileges(String absPath)
+                       throws PathNotFoundException, RepositoryException {
+               return super.getPrivileges(absPath);
+       }
+
+       @Override
+       public boolean hasPrivileges(String absPath, Privilege[] privileges)
+                       throws PathNotFoundException, RepositoryException {
+               return super.hasPrivileges(absPath, privileges);
+       }
+
+}
diff --git a/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java
new file mode 100644 (file)
index 0000000..978be43
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.jackrabbit;
+
+import java.security.Principal;
+import java.util.Set;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.security.auth.Subject;
+import javax.security.auth.x500.X500Principal;
+
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.core.DefaultSecurityManager;
+import org.apache.jackrabbit.core.security.AMContext;
+import org.apache.jackrabbit.core.security.AccessManager;
+import org.apache.jackrabbit.core.security.SecurityConstants;
+import org.apache.jackrabbit.core.security.authorization.WorkspaceAccessManager;
+
+/** Integrates Spring Security and Jackrabbit Security users and roles. */
+public class ArgeoSecurityManager extends DefaultSecurityManager {
+       @Override
+       public AccessManager getAccessManager(Session session, AMContext amContext)
+                       throws RepositoryException {
+               synchronized (getSystemSession()) {
+                       return super.getAccessManager(session, amContext);
+               }
+       }
+
+       @Override
+       public UserManager getUserManager(Session session)
+                       throws RepositoryException {
+               synchronized (getSystemSession()) {
+                       return super.getUserManager(session);
+               }
+       }
+
+       /**
+        * Since this is called once when the session is created, we take the
+        * opportunity to make sure that Jackrabbit users and groups reflect Spring
+        * Security name and authorities.
+        */
+       @Override
+       public String getUserID(Subject subject, String workspaceName)
+                       throws RepositoryException {
+               Set<X500Principal> userPrincipal = subject
+                               .getPrincipals(X500Principal.class);
+               if (userPrincipal.isEmpty())
+                       return super.getUserID(subject, workspaceName);
+               if (userPrincipal.size() > 1) {
+                       StringBuilder buf = new StringBuilder();
+                       for (X500Principal principal : userPrincipal)
+                               buf.append(' ').append('\"').append(principal).append('\"');
+                       throw new RuntimeException("Multiple user principals:" + buf);
+               }
+               return userPrincipal.iterator().next().getName();
+               // Authentication authentication = SecurityContextHolder.getContext()
+               // .getAuthentication();
+               // if (authentication != null)
+               // return authentication.getName();
+               // else
+               // return super.getUserID(subject, workspaceName);
+       }
+
+       @Override
+       protected WorkspaceAccessManager createDefaultWorkspaceAccessManager() {
+               WorkspaceAccessManager wam = super
+                               .createDefaultWorkspaceAccessManager();
+               return new ArgeoWorkspaceAccessManagerImpl(wam);
+       }
+
+       private class ArgeoWorkspaceAccessManagerImpl implements SecurityConstants,
+                       WorkspaceAccessManager {
+               private final WorkspaceAccessManager wam;
+
+               public ArgeoWorkspaceAccessManagerImpl(WorkspaceAccessManager wam) {
+                       super();
+                       this.wam = wam;
+               }
+
+               public void init(Session systemSession) throws RepositoryException {
+                       wam.init(systemSession);
+               }
+
+               public void close() throws RepositoryException {
+               }
+
+               public boolean grants(Set<Principal> principals, String workspaceName)
+                               throws RepositoryException {
+                       // TODO: implements finer access to workspaces
+                       return true;
+               }
+       }
+
+}
diff --git a/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java
new file mode 100644 (file)
index 0000000..62f8fa0
--- /dev/null
@@ -0,0 +1,62 @@
+package org.argeo.security.jackrabbit;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+import javax.security.auth.x500.X500Principal;
+
+import org.apache.jackrabbit.core.security.SecurityConstants;
+import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
+import org.argeo.node.DataAdminPrincipal;
+
+public class SystemJackrabbitLoginModule implements LoginModule {
+
+       private Subject subject;
+
+       @Override
+       public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
+                       Map<String, ?> options) {
+               this.subject = subject;
+       }
+
+       @Override
+       public boolean login() throws LoginException {
+               return true;
+       }
+
+       @Override
+       public boolean commit() throws LoginException {
+               Set<DataAdminPrincipal> initPrincipal = subject.getPrincipals(DataAdminPrincipal.class);
+               if (!initPrincipal.isEmpty()) {
+                       subject.getPrincipals().add(new AdminPrincipal(SecurityConstants.ADMIN_ID));
+                       return true;
+               }
+
+               Set<X500Principal> userPrincipal = subject.getPrincipals(X500Principal.class);
+               if (userPrincipal.isEmpty())
+                       throw new LoginException("Subject must be pre-authenticated");
+               if (userPrincipal.size() > 1)
+                       throw new LoginException("Multiple user principals " + userPrincipal);
+
+               return true;
+       }
+
+       @Override
+       public boolean abort() throws LoginException {
+               return true;
+       }
+
+       @Override
+       public boolean logout() throws LoginException {
+               Set<DataAdminPrincipal> initPrincipal = subject.getPrincipals(DataAdminPrincipal.class);
+               if (!initPrincipal.isEmpty()) {
+                       subject.getPrincipals(AdminPrincipal.class);
+                       return true;
+               }
+               return true;
+       }
+}
diff --git a/org.argeo.ext.rap.ui.workbench/.project b/org.argeo.ext.rap.ui.workbench/.project
new file mode 100644 (file)
index 0000000..751e9d7
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.eclipse.ui.workbench.rap</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments />
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments />
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+       </natures>
+</projectDescription>
diff --git a/org.argeo.ext.rap.ui.workbench/META-INF/spring/osgi.xml b/org.argeo.ext.rap.ui.workbench/META-INF/spring/osgi.xml
new file mode 100644 (file)
index 0000000..206a72a
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
+       xmlns:util="http://www.springframework.org/schema/util"\r
+       xsi:schemaLocation="http://www.springframework.org/schema/osgi  \r
+       http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
+       http://www.springframework.org/schema/beans   \r
+       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\r
+       http://www.springframework.org/schema/util\r
+       http://www.springframework.org/schema/util/spring-util-2.5.xsd">\r
+\r
+       <service interface="org.eclipse.rap.rwt.application.ApplicationConfiguration">\r
+               <service-properties>\r
+                       <beans:entry key="contextName" value="ui" />\r
+               </service-properties>\r
+               <beans:bean\r
+                       class="org.eclipse.rap.ui.internal.servlet.WorkbenchApplicationConfiguration" />\r
+       </service>\r
+</beans:beans>
\ No newline at end of file
diff --git a/org.argeo.ext.rap.ui.workbench/bnd.bnd b/org.argeo.ext.rap.ui.workbench/bnd.bnd
new file mode 100644 (file)
index 0000000..48c602a
--- /dev/null
@@ -0,0 +1,4 @@
+Bundle-SymbolicName: org.argeo.ext.rap.ui.workbench;singleton:=true
+Bundle-ActivationPolicy: lazy
+
+Fragment-Host: org.eclipse.rap.ui.workbench
diff --git a/org.argeo.ext.rap.ui.workbench/build.properties b/org.argeo.ext.rap.ui.workbench/build.properties
new file mode 100644 (file)
index 0000000..485b266
--- /dev/null
@@ -0,0 +1,2 @@
+source.. = src/,\
+           ext/test/
diff --git a/org.argeo.ext.rap.ui.workbench/pom.xml b/org.argeo.ext.rap.ui.workbench/pom.xml
new file mode 100644 (file)
index 0000000..d45618a
--- /dev/null
@@ -0,0 +1,11 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <version>2.1.46-SNAPSHOT</version>
+               <artifactId>argeo-commons</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.ext.rap.ui.workbench</artifactId>
+       <name>Extensions Eclipse RAP Workbench</name>
+</project>
diff --git a/org.argeo.jcr/.classpath b/org.argeo.jcr/.classpath
new file mode 100644 (file)
index 0000000..a8a298a
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="src" path="ext/test"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.argeo.jcr/.project b/org.argeo.jcr/.project
new file mode 100644 (file)
index 0000000..e432547
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.jcr</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/org.argeo.jcr/bnd.bnd b/org.argeo.jcr/bnd.bnd
new file mode 100644 (file)
index 0000000..5645d30
--- /dev/null
@@ -0,0 +1,12 @@
+Import-Package: junit.framework;resolution:=optional,\
+org.xml.sax;version="0.0.0",\
+org.springframework.core;resolution:=optional,\
+org.springframework.core.io;resolution:=optional,\
+org.springframework.*;resolution:=optional,\
+org.apache.jackrabbit.*;resolution:=optional,\
+org.apache.jackrabbit.webdav.jcr;resolution:=optional,\
+org.apache.jackrabbit.webdav.server;resolution:=optional,\
+org.h2;resolution:=optional,\
+org.postgresql;resolution:=optional,\
+*
+Export-Package: org.argeo.jcr.*, org.argeo.jackrabbit.*
\ No newline at end of file
diff --git a/org.argeo.jcr/build.properties b/org.argeo.jcr/build.properties
new file mode 100644 (file)
index 0000000..f4baf37
--- /dev/null
@@ -0,0 +1,23 @@
+source.. = src/,\
+           ext/test/
+output.. = bin/
+bin.includes = META-INF/,\
+               .
+additional.bundles = org.junit,\
+                     org.apache.jackrabbit.core,\
+                     javax.jcr,\
+                     org.apache.jackrabbit.api,\
+                     org.apache.jackrabbit.data,\
+                     org.apache.jackrabbit.jcr.commons,\
+                     org.apache.jackrabbit.spi,\
+                     org.apache.jackrabbit.spi.commons,\
+                     org.slf4j.api,\
+                     org.slf4j.commons.logging,\
+                     org.slf4j.log4j12,\
+                     org.apache.log4j,\
+                     org.apache.commons.collections,\
+                     EDU.oswego.cs.dl.util.concurrent,\
+                     org.apache.lucene,\
+                     org.apache.tika.core,\
+                     org.apache.commons.dbcp,\
+                     org.apache.commons.pool
diff --git a/org.argeo.jcr/ext/test/log4j.properties b/org.argeo.jcr/ext/test/log4j.properties
new file mode 100644 (file)
index 0000000..3d75289
--- /dev/null
@@ -0,0 +1,14 @@
+log4j.rootLogger=WARN, console
+
+## Levels
+log4j.logger.org.argeo=DEBUG
+log4j.logger.org.apache.jackrabbit=OFF
+
+## Appenders
+# console is set to be a ConsoleAppender.
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+
+# console uses PatternLayout.
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+#log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n
+log4j.appender.console.layout.ConversionPattern=%m%n
diff --git a/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/DocBookModelTest.java b/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/DocBookModelTest.java
new file mode 100644 (file)
index 0000000..5af20ba
--- /dev/null
@@ -0,0 +1,115 @@
+package org.argeo.jcr.docbook;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.commons.cnd.CndImporter;
+import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase;
+import org.argeo.jcr.JcrUtils;
+
+public class DocBookModelTest extends AbstractJackrabbitTestCase {
+       private final static Log log = LogFactory.getLog(DocBookModelTest.class);
+
+       public void testLoadWikipediaSample() throws Exception {
+               importXml("WikipediaSample.dbk.xml");
+       }
+
+       public void XXXtestLoadHowTo() throws Exception {
+               importXml("howto.xml", false);
+       }
+
+       protected void importXml(String res) throws Exception {
+               importXml(res, true);
+       }
+
+       protected void importXml(String res, Boolean mini) throws Exception {
+               byte[] bytes;
+               try (InputStream in = getClass().getResourceAsStream(res)) {
+                       bytes = IOUtils.toByteArray(in);
+               }
+
+               {// cnd
+                       long begin = System.currentTimeMillis();
+                       if (mini) {
+                               InputStreamReader reader = new InputStreamReader(getClass()
+                                               .getResourceAsStream(
+                                                               "/org/argeo/jcr/docbook/docbook.cnd"));
+                               CndImporter.registerNodeTypes(reader, session());
+                               reader.close();
+                       } else {
+                               InputStreamReader reader = new InputStreamReader(getClass()
+                                               .getResourceAsStream(
+                                                               "/org/argeo/jcr/docbook/docbook-full.cnd"));
+                               CndImporter.registerNodeTypes(reader, session());
+                               reader.close();
+                       }
+                       long duration = System.currentTimeMillis() - begin;
+                       if (log.isDebugEnabled())
+                               log.debug(" CND loaded in " + duration + " ms");
+               }
+
+               String testPath = "/" + res;
+               // if (mini)
+               JcrUtils.mkdirs(session(), testPath, "dbk:set");
+               // else
+               // JcrUtils.mkdirs(session(), testPath, "dbk:book");
+
+               DocBookModel model = new DocBookModel(session());
+               try (InputStream in = new ByteArrayInputStream(bytes)) {
+                       long begin = System.currentTimeMillis();
+                       model.importXml(testPath, in);
+                       long duration = System.currentTimeMillis() - begin;
+                       if (log.isDebugEnabled())
+                               log.debug("Imported " + res + " " + (bytes.length / 1024l)
+                                               + " kB in " + duration + " ms ("
+                                               + (bytes.length / duration) + " B/ms)");
+               }
+
+               saveSession();
+               // JcrUtils.debug(session().getRootNode());
+
+               try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+                       try {
+                               model.exportXml(testPath + "/dbk:book", out);
+                       } catch (Exception e) {
+                               model.exportXml(testPath + "/dbk:article", out);
+                       }
+                       bytes = out.toByteArray();
+
+                       session().logout();
+                       model.setSession(session());
+
+                       // log.debug(new String(bytes));
+                       try (InputStream in = new ByteArrayInputStream(bytes)) {
+                               long begin = System.currentTimeMillis();
+                               model.importXml(testPath, in);
+                               long duration = System.currentTimeMillis() - begin;
+                               if (log.isDebugEnabled())
+                                       log.debug("Re-imported " + res + " "
+                                                       + (bytes.length / 1024l) + " kB in " + duration
+                                                       + " ms (" + (bytes.length / duration) + " B/ms)");
+                       }
+               }
+               saveSession();
+       }
+
+       protected void saveSession() throws RepositoryException {
+               long begin = System.currentTimeMillis();
+               session().save();
+               long duration = System.currentTimeMillis() - begin;
+               if (log.isDebugEnabled())
+                       log.debug(" Session save took " + duration + " ms");
+       }
+
+       // public static Test suite() {
+       // return defaultTestSuite(DocBookModelTest.class);
+       // }
+
+}
diff --git a/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/WikipediaSample.dbk.xml b/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/WikipediaSample.dbk.xml
new file mode 100644 (file)
index 0000000..29f5b70
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<book xmlns="http://docbook.org/ns/docbook">
+       <title>Very simple book</title>
+       <chapter>
+               <title>Chapter 1</title>
+               <para>Hello world!</para>
+               <para>I hope that your day is proceeding <emphasis>splendidly</emphasis>!</para>
+       </chapter>
+       <chapter>
+               <title>Chapter 2</title>
+               <para>Hello again, world!</para>
+       </chapter>
+</book>
\ No newline at end of file
diff --git a/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/howto.xml b/org.argeo.jcr/ext/test/org/argeo/jcr/docbook/howto.xml
new file mode 100644 (file)
index 0000000..b8b022a
--- /dev/null
@@ -0,0 +1,2295 @@
+<?xml version="1.0" encoding="utf-8"?>  <!-- -*- nxml -*- -->
+<!DOCTYPE book [
+<!ENTITY version "5.0">
+<!--
+<!ENTITY yes "<phrase dbk:role='unicode yes'>✔</phrase>">
+<!ENTITY no "<phrase dbk:role='unicode no'>✘</phrase>">
+-->
+<!ENTITY yes "<phrase dbk:role='unicode yes'>YES</phrase>">
+<!ENTITY no "<phrase dbk:role='unicode no'>NO</phrase>">
+]>
+<book xmlns="http://docbook.org/ns/docbook"  xmlns:dbk="http://docbook.org/ns/docbook"
+        xmlns:xl="http://www.w3.org/1999/xlink" xml:lang="en">
+<article>
+<info>
+<title>DocBook V5.0</title>
+<subtitle>The Transition Guide</subtitle>
+
+<authorgroup>
+<author><personname>Jirka Kosek</personname>
+        <email>jirka@kosek.cz</email></author>
+<author><personname>Norman Walsh</personname>
+        <email>ndw@nwalsh.com</email>
+        <contrib>§convert4to5, proofreading</contrib></author>
+<author><personname>Dick Hamilton</personname>
+        <email>rlhamilton@frii.com</email>
+        <contrib>§changes-removed, customization, proofreading</contrib></author>
+<othercredit
+  dbk:class="other"
+  dbk:otherclass="contributor"
+  ><personname>Michael(tm) Smith</personname>
+  <email>smith@sideshowbarker.net</email>
+  <contrib>§dbxsl-ns</contrib>
+</othercredit>
+</authorgroup>
+
+<pubdate>2009-06-16</pubdate>
+<pubdate>2008-02-06</pubdate>
+<pubdate>2007-10-28</pubdate>
+<pubdate>2006-10-22</pubdate>
+<pubdate>2006-05-16</pubdate>
+<pubdate>2006-03-01</pubdate>
+<pubdate>2005-12-28</pubdate>
+<pubdate>2005-10-27</pubdate>
+
+</info>
+
+<para>This document is targeted at DocBook users who are considering
+switching from DocBook V4.x to DocBook V5.0. It describes
+differences between DocBook V4.x and V5.0 and provides some suggestions about
+how to edit and process DocBook V5.0 documents. There is
+also a section devoted to conversion of legacy documents from DocBook
+4.x to DocBook V5.0.</para>
+
+<para>At the time this was written the current version of DocBook V5.0
+was &version;. However, almost all of the information in this document is
+general and applies to any newer version of DocBook V5.0.
+</para>
+
+<section xml:id="introduction">
+<title>Introduction</title>
+
+<para>The differences between DocBook V4.x and V5.0 are quite radical in
+some aspects, but the basic idea behind DocBook is still the same, and
+almost all element names are unchanged. Because of this it is very
+easy to become familiar with DocBook V5.0 if you know any previous version of
+DocBook. You can find a complete list of changes in
+<citation>DB5SPEC</citation>, here we will discuss only the most
+fundamental changes.</para>
+
+<section xml:id="introduction-ns">
+<title>Finally in a namespace</title>
+
+<para>All DocBook V5.0 elements are in the namespace
+<uri>http://docbook.org/ns/docbook</uri>. <acronym>XML<alt>Extensible
+Markup Language</alt></acronym> namespaces are used to distinguish
+between different element sets. In the last few years, almost all new
+XML grammars have used their own namespace. It is easy to
+create compound documents that contain elements from different XML
+vocabularies. DocBook V5.0 is following this design rule. Using
+namespaces in your documents is very easy. Consider this
+simple article marked up in DocBook V4.5:</para>
+
+<programlisting><![CDATA[<article>
+  <title>Sample article</title>
+  <para>This is a really short article.</para>
+</article>]]></programlisting>
+
+<para>The corresponding DocBook V5.0 article will look very similar:</para>
+
+<programlisting><![CDATA[<article xmlns="http://docbook.org/ns/docbook" …>
+  <title>Sample article</title>
+  <para>This is a really short article.</para>
+</article>]]></programlisting>
+
+<para>The only change is the addition of a default namespace declaration
+(<code>xmlns="http://docbook.org/ns/docbook"</code>) on the root
+element. This declaration applies the namespace to the root element and
+all nested elements. Each
+element is now uniquely identified by its local name and namespace.</para>
+
+<note>
+<para>The namespace name <uri>http://docbook.org/ns/docbook</uri> serves
+only as an identifier. This resource is not fetched during processing
+of DocBook documents, and you are not required to have an Internet
+connection during processing. If you access the namespace URI with a browser,
+you will find a short explanatory document about the namespace. In the
+future this document will probably conform to (some version of) RDDL
+and provide pointers to related resources.</para>
+</note>
+
+</section>
+
+<section xml:id="introduction-rng">
+<title>Relaxing with DocBook</title>
+
+<para>For more than a decade, the DocBook schema was defined using a
+DTD. However, DTDs have serious limitations, and DocBook V5.0 is thus
+defined using a very powerful schema language called RELAX NG. Thanks
+to RELAX NG, it is now much easier to create customized versions of
+DocBook, and some content models are now cleaner and more
+precise.</para>
+
+<para>Using RELAX NG has an impact on the document prolog. The following
+example shows the typical prolog of a DocBook V4.x document. The version of
+the DocBook DTD (in this case 4.5) is indicated in the document type
+declaration (!DOCTYPE) which points to a particular version of the
+DTD.</para>
+
+<example xml:id="ex.docbook45">
+<title>DocBook V4.5 document</title>
+<programlisting><![CDATA[<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE article PUBLIC '-//OASIS//DTD DocBook XML V4.5//EN'
+                         'http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd'>
+<article lang="en">
+  <title>Sample article</title>
+  <para>This is a very short article.</para>
+</article>]]></programlisting>
+</example>
+
+<para>In contrast, DocBook V5.0 does not depend on DTDs anymore. This
+mean that there is no document type declaration and the version of DocBook
+used is indicated with the <tag dbk:class="attribute">version</tag>
+attribute instead.</para>
+
+<example xml:id="ex.docbook5">
+<title>DocBook V5.0 document</title>
+<programlisting><![CDATA[<?xml version="1.0" encoding="utf-8"?>
+<article xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
+  <title>Sample article</title>
+  <para>This is a very short article.</para>
+</article>]]></programlisting>
+</example>
+
+<para>As you can see, DocBook V5.0 is built on top of existing XML
+standards as much as possible, for example the <tag
+dbk:class="attribute">lang</tag> attribute is superseded by the standard
+<tag xl:href="http://www.w3.org/TR/REC-xml/#sec-lang-tag"
+dbk:class="attribute">xml:lang</tag> attribute.</para>
+
+<para>Another fundamental change is that there is no direct indication
+of the schema used. Later in this document, you will learn how you can
+specify a schema to be used for document validation.</para>
+
+<note>
+<para>Although we recommend the RELAX NG schema for DocBook
+V5.0, there are also DTD and W3C XML Schema versions available (see <xref
+dbk:linkend="schemas"/>) for tools that do not yet support RELAX NG.</para>
+</note>
+
+</section>
+
+<section xml:id="introduction-why-to-switch">
+<title>Why switch to DocBook V5.0?</title>
+
+<para>The simple answer is <quote>because DocBook V5.0 is the
+future</quote>. Apart from this marketing blurb, there are also more
+technical reasons:</para>
+
+<itemizedlist>
+<listitem>
+<para><emphasis>DocBook V4.x is feature frozen.</emphasis>DocBook V4.5
+is the last version of DocBook in the V4.x series. Any new DocBook
+development, like the addition of new elements, will be done in
+DocBook V5.0. It is only matter of time before useful, new elements
+will be added into DocBook V5.0, but they are not likely to be back
+ported into DocBook V4.x. DocBook V4.x will be in maintenance mode and
+errata will be published if necessary. </para>
+</listitem>
+<listitem>
+<para><emphasis>DocBook V5.0 offers new functionality.</emphasis>
+DocBook V5.0 provides significant improvements over DocBook V4.x. For
+example there is general markup for annotations, a new and flexible
+system for linking, and unified markup for information sections using
+the <tag>info</tag> element.</para>
+</listitem>
+<listitem>
+<para><emphasis>DocBook V5.0 is more extensible.</emphasis> Having
+DocBook V5.0 in a separate namespace allows you to easily mix DocBook
+markup with other XML-based languages like SVG, MathML, XHTML or even
+FooBarML.</para>
+</listitem>
+<listitem>
+<para><emphasis>DocBook V5.0 is easier to customize.</emphasis> RELAX
+NG offers many powerful constructs that make customization much easier
+than it would be using a DTD (see <xref dbk:linkend="customizations"/>).</para>
+</listitem>
+</itemizedlist>
+
+</section>
+
+<section xml:id="introduction-schemas">
+<title>Schema jungle</title>
+
+<para>Schemas for DocBook V5.0 are available in several formats at
+<link xl:href="http://www.oasis-open.org/docbook/xml/&version;/"/> (or the
+mirror at <link xl:href="http://docbook.org/xml/&version;/"/>).
+Only the RELAX NG schema is normative
+and it is preferred over the other schema languages.  However, for your
+convenience there are also DTD and W3C XML Schema versions provided for DocBook
+V5.0. But please note that neither the DTD nor the W3C XML schema are able to
+capture all the constraints of DocBook V5.0. This mean that a
+document that validates against the DTD or XML schema is not necessarily
+valid against the RELAX NG schema and thus may not be a valid
+DocBook V5.0 document. See <xref dbk:linkend="t.schema-comparison"/> for
+summary of constraints that are checked by different schemas.</para>
+
+<para>DTD and W3C XML Schema versions of the DocBook V5.0 grammar are provided
+as a convenience for users who want to use DocBook V5.0 with legacy tools
+that don't support RELAX NG. Authors are encouraged to switch to RELAX
+NG based tools as soon as possible, or at least to validate documents
+against the RELAX NG schema before further processing.</para>
+
+<para>Some document constraints can't be expressed in schema languages
+like RELAX NG or W3C XML Schema. To check for these additional
+constraints DocBook V5.0 uses Schematron.  We recommend that you
+validate your document against both the RELAX NG and
+Schematron schemas.</para>
+
+<table xml:id="t.schema-comparison">
+  <title>Schema Comparison</title>
+  <tgroup dbk:cols="6">
+    <colspec dbk:colwidth="4*"/>
+    <colspec dbk:colwidth="1*" dbk:align="center"/>
+    <colspec dbk:colwidth="1*" dbk:align="center"/>
+    <colspec dbk:colwidth="1*" dbk:align="center"/>
+    <colspec dbk:colwidth="1*" dbk:align="center"/>
+    <colspec dbk:colwidth="1*" dbk:align="center"/>
+    <thead>
+      <row>
+       <entry>Description</entry>
+       <entry>DTD</entry>
+       <entry>W3C XML Schema</entry>
+       <entry>W3C XML Schema + Schematron</entry>
+       <entry>RELAX NG</entry>
+       <entry>RELAX NG + Schematron/NVDL</entry>
+      </row>
+    </thead>
+    <tbody>
+      <row>
+       <entry>Basic document structure</entry>
+       <entry>&yes;</entry>
+       <entry>&yes;</entry>
+       <entry>&yes;</entry>
+       <entry>&yes;</entry>
+       <entry>&yes;</entry>
+      </row>
+      <row>
+       <entry>ID/IDREF datatypes</entry>
+       <entry>&yes;</entry>
+       <entry>&yes;</entry>
+       <entry>&yes;</entry>
+       <entry>&yes;</entry>
+       <entry>&yes;</entry>
+      </row>
+      <row>
+       <entry>Datatypes<footnote>
+         <para>In a very few places RELAX NG specifies datatype
+         like number (mainly for length specifications) or
+         enumeration between <literal>0</literal> and
+         <literal>1</literal>.</para>
+         <para>In general those datatypes can be also supported in
+         W3C XML Schema, but currently this schema is generated
+         from DTD which lacks datatype information.</para>
+       </footnote>
+       </entry>
+       <entry>&no;</entry>
+       <entry>&no;</entry>
+       <entry>&no;</entry>
+       <entry>&yes;</entry>
+       <entry>&yes;</entry>
+      </row>
+      <row>
+       <entry>Co-occurrences<footnote>
+       <para>RELAX NG grammar enforces exclusivity of several
+       elements. For example if you have <tag>title</tag> inside
+       <tag>info</tag> then it is not allowed to have another
+       <tag>title</tag> outside <tag>info</tag>. Similarly,
+       models of HTML and CALS tables are separated and validated
+       properly, where in DTD and WXS only union of both models is
+       available.</para>
+       <para>On other places co-occurrences enforces particular
+       content model based on presence of specific attribute or
+       attribute value.</para>
+       <para>Please also note that in theory co-occurences can be
+       validated using Schematron, but the current DocBook schema
+       uses RELAX NG for these definitions. Schematron can be used
+       only for validation, whereas grammar based schemas like
+       RELAX NG are useful also for other purposes like guided editing.</para>
+       </footnote></entry>
+       <entry>&no;</entry>
+       <entry>&no;</entry>
+       <entry>&no;</entry>
+       <entry>&yes;</entry>
+       <entry>&yes;</entry>
+      </row>
+      <row>
+       <entry>Hooks for MathML and SVG content</entry>
+       <entry>&no;</entry>
+       <entry>&no;</entry>
+       <entry>&no;</entry>
+       <entry>&yes;</entry>
+       <entry>&yes;</entry>
+      </row>
+      <row>
+       <entry>Link type integrity<footnote>
+       <para>Check whether ID/IDREF links are pointing to element
+       of corresponding type. For example that
+       <tag>footnoteref</tag> points to
+       <tag>footnote</tag>.</para></footnote></entry>
+       <entry>&no;</entry>
+       <entry>&no;</entry>
+       <entry>&yes;</entry>
+       <entry>&no;</entry>
+       <entry>&yes;</entry>
+      </row>
+      <row>
+       <entry>Presence of <tag dbk:class="attribute">version</tag>
+       attribute on the root element</entry>
+       <entry>&no;</entry>
+       <entry>&no;</entry>
+       <entry>&yes;</entry>
+       <entry>&no;</entry>
+       <entry>&yes;</entry>
+      </row>
+      <row>
+       <entry>Miscellaneous checks<footnote>
+       <para>For example consistency of segmented lists, only one
+       term inside term definition etc.</para></footnote></entry>
+       <entry>&no;</entry>
+       <entry>&no;</entry>
+       <entry>&yes;</entry>
+       <entry>&no;</entry>
+       <entry>&yes;</entry>
+      </row>
+      <row>
+       <entry>Element exclusions<footnote>
+       <para>Prevents improper nesting of elements, like admonition
+       inside admonition.</para></footnote></entry>
+       <entry>&no;</entry>
+       <entry>&no;</entry>
+       <entry>&yes;</entry>
+       <entry>&no;</entry>
+       <entry>&yes;</entry>
+      </row>        
+    </tbody>
+  </tgroup>
+</table>
+
+<section xml:id="schemas">
+<title>Where to get the schemas</title>
+
+<para>The latest versions of schemas can be obtained from <link
+xl:href="http://docbook.org/schemas/5x.html"/>. At the time this was
+written the latest version was &version;. Individual schemas are
+available at the following locations:</para>
+
+<variablelist>
+<varlistentry>
+<term>RELAX NG schema</term>
+<listitem><para><link xl:href="http://docbook.org/xml/&version;/rng/docbook.rng"/></para></listitem>
+</varlistentry>
+<varlistentry>
+<term>RELAX NG schema in compact syntax</term>
+<listitem><para><link xl:href="http://docbook.org/xml/&version;/rng/docbook.rnc"/></para></listitem>
+</varlistentry>
+<varlistentry>
+<term>DTD</term>
+<listitem><para><link xl:href="http://docbook.org/xml/&version;/dtd/docbook.dtd"/></para></listitem>
+</varlistentry>
+<varlistentry>
+<term>W3C XML Schema</term>
+<listitem><para><link xl:href="http://docbook.org/xml/&version;/xsd/docbook.xsd"/></para></listitem>
+</varlistentry>
+<varlistentry>
+<term>Schematron schema with additional checks</term>
+<listitem><para><link xl:href="http://docbook.org/xml/&version;/sch/docbook.sch"/></para></listitem>
+</varlistentry>
+</variablelist>
+
+<para>These schemas are also available from the mirror at
+<link xl:href="http://www.oasis-open.org/docbook/xml/&version;/"/>.</para>
+
+</section>
+
+<section xml:id="docs">
+<title>DocBook documentation</title>
+
+<para>Detailed documentation about each DocBook V5.0 element is
+presented in <link
+xl:href="http://docbook.org/tdg5/en/html/pt02.html">the reference part
+of <citetitle>DocBook: The Definitive Guide</citetitle></link>.</para>
+
+<note>
+<para>Other parts of <citetitle>DocBook: The Definitive
+Guide</citetitle> have not yet been updated to reflect the changes
+made in DocBook V5.0. Please do not be confused by this.</para>
+</note>
+
+</section>
+
+</section>
+
+</section>
+
+<section xml:id="tools">
+<title>Tool chain</title>
+
+<para>This section briefly describes tools and procedures to edit and
+process content stored in DocBook V5.0.</para>
+
+<section xml:id="editors">
+<title>Editing DocBook V5.0</title>
+
+<para>Because DocBook is an XML-based format and XML is a text-based
+format, you can use any text editor to create and edit DocBook V5.0
+documents. However, using <quote>dumb</quote> editors like Notepad is
+not very productive. You will do better if you use an editor that
+supports XML. Although there are DTD and W3C XML Schemas available for
+DocBook V5.0, which means you can use any editor that works with DTDs
+or W3C XML Schemas, we recommend that you use the RELAX NG grammar
+with DocBook V5.0. The rest of this section contains an overview of
+XML editors (listed in alphabetical order) that are known to work with
+RELAX NG schemas and that offer guided editing based on the RELAX NG
+schema.</para>
+
+<section xml:id="editors-nxml">
+<title>Emacs and nXML</title>
+
+<para><link xl:href="http://www.thaiopensource.com/nxml-mode/">nXML
+mode</link> is an add-on for the <application
+xl:href="http://www.gnu.org/software/emacs/emacs.html">GNU
+Emacs</application> text editor. By installing nXML you can turn Emacs
+into a very powerful XML editor that offers guided editing and
+validation of XML documents.</para>
+
+<figure xml:id="f.emacs">
+<title>Emacs with nXML mode provides guided editing and validation</title>
+<mediaobject>
+<imageobject dbk:role="html">
+<imagedata dbk:fileref="images/emacs.png"/>
+</imageobject>
+<imageobject dbk:role="fo">
+<imagedata dbk:fileref="images/emacs.png" dbk:width="100%"/>
+</imageobject>
+</mediaobject>
+</figure>
+
+<para>nXML uses a special configuration file named
+<filename>schemas.xml</filename> to associate schemas with XML
+documents. Often you will find this file in the directory
+<filename>site-lisp/nxml/schema</filename> inside the Emacs installation
+directory. Adding the following line into the configuration file,
+will associate DocBook V5.0 elements with the appropriate
+schema:</para>
+
+<programlisting>&lt;namespace ns="http://docbook.org/ns/docbook" uri="<replaceable>/path/to/</replaceable>docbook.rnc"/></programlisting>
+
+<note>
+<para>Please note that nXML ships with a file named
+<filename>docbook.rnc</filename>. This file contains the RELAX NG grammar
+for DocBook V4.x. Be sure that you associate the DocBook V5.0 namespace
+with the corresponding DocBook V5.0 grammar.</para>
+</note>
+
+<para>If you can't edit the global <filename>schemas.xml</filename> file,
+you can create this file in the same directory as your document. nXML will
+find associations placed there also. In this case you must create a
+complete configuration file like:</para>
+
+<programlisting>&lt;locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0">
+  &lt;namespace ns="http://docbook.org/ns/docbook" uri="<replaceable>/path/to/</replaceable>docbook.rnc"/>
+&lt;/locatingRules></programlisting>
+
+</section>
+
+<section xml:id="editors-oxygen">
+<title>oXygen</title>
+
+<para><application
+xl:href="http://www.oxygenxml.com/">oXygen</application> is a feature
+rich XML editor. It has built-in support for many schema languages
+including RELAX NG and it is preconfigured with many document types
+including DocBook. oXygen will assist you with writing DocBook V5.0
+content, and you will be able to validate your documents against both
+RELAX NG and Schematron schemas.</para>
+
+<figure xml:id="f.oxygen.open5">
+<title>DocBook V5.0 document opened in oXygen</title>
+<mediaobject>
+<imageobject>
+<imagedata dbk:fileref="images/oxygen4.png" dbk:width="100%"/>
+</imageobject>
+</mediaobject>
+</figure>
+
+<figure xml:id="f.oxygen.author.mode">
+<title>DocBook V5.0 document opened in oXygen in Author mode</title>
+<mediaobject>
+<imageobject>
+<imagedata dbk:fileref="images/oxygen5.png" dbk:width="100%"/>
+</imageobject>
+</mediaobject>
+</figure>
+
+</section>
+
+<section xml:id="editors-xxe">
+<title>XML Mind XML editor</title>
+
+<para><application xl:href="http://www.xmlmind.com/xmleditor/">XML
+Mind XML editor</application> (XXE) is a visual validating XML editor that
+provides a wordprocessor-like interface to users. It is available in
+two versions, Standard and Professional. The Standard version is free and
+provides everything you need to edit DocBook V5.0 documents.</para>
+
+<figure xml:id="f.xmlmind">
+<title>XML Mind XML Editor – feels almost like MS Word but real DocBook V5.0 markup is created</title>
+<mediaobject>
+<imageobject>
+<imagedata dbk:fileref="images/xxe.png" dbk:width="100%"/>
+</imageobject>
+</mediaobject>
+</figure>
+
+<para>In order to use DocBook V5.0 in XXE you have to install
+an add-on. Go to
+<menuchoice><guimenu>Options</guimenu><guimenuitem>Install
+Add-ons…</guimenuitem></menuchoice>. Then choose <guilabel>DocBook
+5 configuration</guilabel> and press the <guibutton>OK</guibutton>
+button. After restart, XXE is ready to work with DocBook V5.0
+documents.</para>
+
+</section>
+
+</section>
+
+<section xml:id="validators">
+<title>Validating DocBook V5.0</title>
+
+<para>If you are not using a RELAX NG-based validating editor when you
+create documents, we strongly recommend that you validate your
+documents against RELAX NG and Schematron schemas before processing
+them. Only after successful validation can you be sure that your
+document is really DocBook V5.0 and that processing tools will be able
+to process it correctly.</para>
+
+<para>For validation you can use tools that support simultaneous RELAX NG and
+Schematron validation, or you can use NVDL to orchestrate validation using
+the two schemas.</para>
+
+<section xml:id="validators-rng-sch">
+<title>Using RELAX NG and Schematron</title>
+
+<para>You can find a list of RELAX NG validators at <link
+xl:href="http://relaxng.org/#validators"/>. It is best to use
+validators with support for embedded Schematron rules inside RELAX NG
+schemas. Schematron is a rule-based validation language which is used
+to impose additional constraints on DocBook documents. Schematron rules
+assert conditions which are impossible or difficult to express 
+in a pure RELAX NG schema.</para>
+
+<para><application xl:href="https://msv.dev.java.net/">Sun 
+Multi-Schema XML Validator (MSV)</application> is able to validate an XML
+document against a RELAX NG schema and Schematron rules at the same time.
+To install and use MSV follow these steps:</para>
+
+<procedure>
+<step>
+<para>Download <filename>relames.zip</filename> from <link xl:href="https://msv.dev.java.net/servlets/ProjectDocumentList?folderID=101"/>.</para>
+</step>
+<step>
+<para>Unpack the downloaded file into an arbitrary directory.</para>
+</step>
+<step>
+<para>Validate your document using the following command:</para>
+<screen><command>java</command> -Xss512K -jar <replaceable>/path/to/</replaceable>relames.jar <replaceable>/path/to/</replaceable>docbook.rng document.xml</screen>
+<note>
+<para>The switch <option>-Xss512K</option> increases the stack size
+of the Java virtual machine. This is necessary because the DocBook schema is
+quite large. If you get stack overflow errors from MSV, increase
+this value. You may get spurious error messages if the value
+is too small, so if you get a stack overflow error, ignore any other error
+messages and try a larger value for the stack size.
+If you are not using Sun's Java implementation, please consult the
+documentation for your virtual machine to learn how to increase the stack
+size.</para>
+</note>
+</step>
+</procedure>
+
+<para>There is also an <link
+xl:href="http://relaxed.vse.cz/docbookvalidator/">on-line DocBook V5.0
+validator</link> that validates DocBook V5.0 documents against the normative
+RELAX NG schema with embedded Schematron rules.</para>
+
+</section>
+
+<section>
+<title>Using NVDL</title>
+
+<para>NVDL is a meta-schema language which can validate a document 
+against several schemas. DocBook V5.0 comes with a NVDL
+schema which specifies that DocBook documents should be validated
+against both RELAX NG and Schematron schemas.</para>
+
+<para>You can find a list of NVDL validators at <link
+xl:href="http://nvdl.org/"/>. The following procedures show how to
+install and use the <application
+xl:href="http://www.oxygenxml.com/onvdl.html">oNVDL</application> and
+<application xl:href="http://jnvdl.sourceforge.net">JNVDL</application>
+validators.</para>
+
+<procedure>
+<title>oNVDL installation and usage</title>
+<step>
+<para>Download <filename
+xl:href="http://www.oxygenxml.com/InstData/onvdl/onvdl-20070517.zip">onvdl-20070517.zip</filename>.</para>
+</step>
+<step>
+<para>Unpack the downloaded file into an arbitrary directory.</para>
+</step>
+<step>
+<para>Validate your document using the following command:</para>
+<screen><command>java</command> -jar <replaceable>/path/to/oNVDL/</replaceable>bin/onvdl.jar <replaceable>/path/to/</replaceable>docbook.nvdl document.xml</screen>
+</step>
+</procedure>
+
+<procedure>
+<title>JNVDL installation and usage</title>
+<step>
+<para>Download the latest release of JNVDL from <link
+xl:href="http://sourceforge.net/project/showfiles.php?group_id=164464"/>.</para>
+</step>
+<step>
+<para>Unpack the downloaded file into an arbitrary directory.</para>
+</step>
+<step>
+<para>Modify file <filename>jnvdl.bat</filename> (or <filename>jnvdl.sh</filename> on Unix based systems) to include <option>-Xss512K</option> switch directly after <command>java</command> command.</para>
+</step>
+<step>
+<para>On Windows systems, validate your document using the following command:</para>
+<screen><replaceable>/path/to/jnvdl/</replaceable><command>jnvdl</command> -nt -s <replaceable>/path/to/</replaceable>docbook.nvdl document.xml</screen>
+<para>On Unix systems, validate your document using the following command:</para>
+<screen><replaceable>/path/to/jnvdl/</replaceable><command>jnvdl.sh</command> -nt -s <replaceable>/path/to/</replaceable>docbook.nvdl document.xml</screen>
+</step>
+</procedure>
+
+</section>
+
+</section>
+
+<section xml:id="processing">
+<title>Processing DocBook V5.0</title>
+
+<para>Part of DocBook's great success can be attributed to the
+availability of free
+tools that can be used to transform DocBook content into various
+target formats including HTML and PDF. The DocBook XSL Stylesheets are
+very popular tools.</para>
+
+<section xml:id="dbxsl">
+<title>DocBook XSL Stylesheets</title>
+
+<para>The DocBook stylesheets are designed to process content written in
+different versions of DocBook (for example 3.1 and 4.2). Recent
+versions of the stylesheets are also able to process DocBook V5.0
+with some limitations.</para>
+
+<para>You can process DocBook V5.0 documents with the DocBook XSL
+stylesheets in exactly the same way you process DocBook V4.x documents.
+You do not need special software; you can stick to your preferred
+XSLT processor, be it Saxon, xsltproc, Xalan or whatever else (but see
+the note about the lost base URI below).</para>
+
+<para>During document processing, the stylesheets strip
+namespaces from DocBook V5.0 to get a document which will be
+very similar to DocBook V4.x. This is necessary because from the XSLT
+point of view, elements from different namespaces are distinct and cannot 
+be easily processed by the same set of templates. This process is
+completely transparent to the user. If you are processing DocBook V5.0
+documents, the only difference is that you will see the following
+additional message:</para>
+
+<screen>Note: namesp. cut : stripped namespace before processing
+Note: namesp. cut : processing stripped document</screen>
+
+<para>Although you can successfully use the existing stylesheets to
+process DocBook V5.0, there are some limitations and unsupported
+features. The unsupported features include:</para>
+
+<itemizedlist>
+<listitem><para>general annotations;</para></listitem>
+<listitem><para>general XLink links on all elements.</para></listitem>
+</itemizedlist>
+
+<note>
+<para>During namespace stripping, the base URI of the document is
+lost. This means that in rare situations, relatively referenced
+resources like images or programlistings can be processed incorrectly.
+The stylesheets attempt to compensate for this problem, but that is not always 
+possible. When an XSLT processor other than Saxon or Xalan is used, a warning 
+message is generated:
+
+<screen>WARNING: cannot add @xml:base to node set root element. Relative paths may not work.</screen>
+</para>
+
+</note>
+</section>
+
+<section xml:id="dbxsl-ns">
+<title>DocBook XSL-NS Stylesheets</title>
+<para>As you can see from reading the previous section, namespace
+  stripping has limitations that will cause trouble in some
+  situations. To overcome those limitations, Bob Stayton created a
+  build system for taking the non-namespace-aware DocBook XSL
+  stylesheets and generating namespace-aware versions from them.
+  The DocBook <link
+    xl:href="http://docbook.sourceforge.net/release/xsl-ns/current/"
+  >XSL-NS stylesheets</link> are the result.</para>
+
+<para>The DocBook XSL-NS stylesheets are released side-by-side
+  with the DocBook XSL stylesheets, as a separate <link
+  xl:href="https://sourceforge.net/project/showfiles.php?group_id=21935&amp;package_id=219178"
+  ><package>docbook-xsl-ns</package></link> package. They are the
+recommended XSLT 1.0 stylesheets to use for transforming
+namespaced (DocBook V5.0) documents.</para>
+</section>
+
+<section xml:id="dbxsl2">
+<title>XSLT 2.0 based re-implementation</title>
+
+<para>XSLT 1.0 is missing some important features. To work around
+these missing features, the current DocBook XSL stylesheets use some
+implementation-specific extensions.
+XSLT 2.0 adds many new and previously missing features into the language.
+A new set of DocBook stylesheets is being implemented based on XSLT 2.0
+to take advantage of these features and to fully support DocBook V5.0.
+</para>
+
+<para>The XSLT 2.0 based stylesheets have many new features, including:</para>
+
+<itemizedlist>
+<listitem><para>seamless integration of profiling (conditional
+documents) with external bibliographies and
+glossaries;</para></listitem>
+<listitem><para>no need for (most) external extensions;</para></listitem>
+<listitem><para>internationalized indexes;</para></listitem>
+<listitem><para>easy to customize titlepage templates.</para></listitem>
+</itemizedlist>
+
+<para>The XSLT 2.0 based stylesheets are still under development.  At
+this writing, they only support HTML and chunked HTML output.  As time
+permits, the stylesheet developers will be adding other formats.  Since
+the stylesheets are developed in the limited free time the developers
+have, there's no specific schedule.</para>
+
+<para>There are not very many XSLT 2.0 implementations available.
+But, if you want to try the new stylesheets, grab a snapshot of
+the development version from <link
+xl:href="http://docbook.sourceforge.net/snapshots/docbook-xsl2-snapshot.zip"/>
+and unpack it somewhere. Then download and install Saxon 9 from <link
+xl:href="http://saxon.sf.net"/>.</para>
+
+<para>To transform a DocBook V5.0 document to a single HTML page use the command:</para>
+
+<screen><command>java</command> -jar <replaceable>/path/to/</replaceable>saxon9.jar -o output.html document.xml <replaceable>/path/to/</replaceable>docbook-xsl2-snapshot/html/docbook.xsl</screen>
+
+<para>To transform a DocBook V5.0 document to a set of chunked HTML pages use the command:</para>
+
+<screen><command>java</command> -jar <replaceable>/path/to/</replaceable>saxon9.jar document.xml <replaceable>/path/to/</replaceable>docbook-xsl2-snapshot/html/chunk.xsl</screen>
+
+</section>
+
+</section>
+
+</section>
+
+<section xml:id="changes">
+<title>Markup changes</title>
+
+<para>This section describes the most common markup changes
+between DocBook V4.x and V5.0.
+You can find a complete list of changes in
+<citation>DB5SPEC</citation>.</para>
+
+<section xml:id="changes-linking">
+<title>Improved cross-referencing and linking</title>
+
+<para>In DocBook V4.x the attribute <tag dbk:class="attribute">id</tag> is
+used to assign a unique identifier to an element. In DocBook V5.0 this
+attribute is renamed <tag dbk:class="attribute">xml:id</tag> in order
+to comply with <citation>XMLID</citation>.</para>
+
+<para>Now you can use almost any inline element as the source of a link,
+not just <tag>xref</tag> or <tag>link</tag>. For example, the following
+DocBook 4.x content:</para> 
+
+<programlisting><![CDATA[<section id="dir">
+  <title>DIR command</title>
+  <para>...</para>
+</section>
+
+<section id="ls">
+  <title>LS command</title>
+  <para>This command is a synonym for <link linkend="dir"><command>DIR</command></link> command.</para>
+</section>]]></programlisting>
+
+<para>is written in DocBook V5.0 as:</para>
+
+<programlisting><![CDATA[<section xml:id="dir">
+  <title>DIR command</title>
+  <para>...</para>
+</section>
+
+<section xml:id="ls">
+  <title>LS command</title>
+  <para>This command is a synonym for <command linkend="dir">DIR</command> command.</para>
+</section>]]></programlisting>
+
+<para>The <tag dbk:class="attribute">linkend</tag> attribute was added to all
+inline elements together with the <tag dbk:class="attribute">href</tag>
+attribute from the XLink namespace. This means that you can use any inline
+element as the source of a hypertext link. To use XLinks you have
+to declare the XLink namespace (most often on the root element of your
+document):</para>
+
+<programlisting><![CDATA[<article xmlns="http://docbook.org/ns/docbook" 
+         xmlns:xl="http://www.w3.org/1999/xlink" version="5.0">
+  <title>Test article</title>
+
+  <para><application xl:href="http://www.gnu.org/software/emacs/emacs.html">Emacs</application> 
+    is my favourite text editor.</para>]]>
+  …</programlisting>
+
+<para>The <tag dbk:condition="v4">ulink</tag> element was removed from DocBook V5.0
+in favor of XLink linking. Instead of the DocBook V4.x <tag dbk:condition="v4">ulink</tag>
+element:</para>
+
+<programlisting><![CDATA[<ulink url="http://docbook.org">DocBook site</ulink>]]></programlisting>
+
+<para>you can now use <tag>link</tag></para>
+
+<programlisting><![CDATA[<link xl:href="http://docbook.org">DocBook site</link>]]></programlisting>
+
+<para>XLink links may contain a fragment identifier, which you can 
+use instead of <tag dbk:class="attribute">linkend</tag> to form
+cross-references inside a document; for example:</para>
+
+<programlisting><![CDATA[<command xl:href="#dir">DIR</command>]]></programlisting>
+
+<para>However XLink links are not checked during validation, while <tag
+dbk:class="attribute">xml:id</tag>/<tag dbk:class="attribute">linkend</tag>
+links are checked for ID/IDREF consistency.
+One place where the XLink-based, fragment identifier scheme is
+useful is when XInclude is being used, since XML ID/IDREF links
+cannot span XInclude boundaries.
+You can use whichever approach better suits your needs.</para>
+</section>
+
+<section xml:id="changes-renamed">
+<title>Renamed elements</title>
+
+<para>Some elements were renamed to better express their meaning or to
+reduce the total number of elements available in DocBook.</para>
+
+<table xml:id="t.renamed">
+<title>Renamed elements</title>
+<tgroup dbk:cols="2">
+<thead>
+<row>
+<entry>Old name</entry>
+<entry>New name</entry>
+</row>
+</thead>
+<tbody>
+<row>
+<entry><tag dbk:condition="v4">sgmltag</tag></entry>
+<entry><tag>tag</tag></entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">bookinfo</tag>, <tag dbk:condition="v4">articleinfo</tag>,
+<tag dbk:condition="v4">chapterinfo</tag>, <tag dbk:condition="nolink">*info</tag></entry>
+<entry><tag>info</tag></entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">authorblurb</tag></entry>
+<entry><tag>personblurb</tag></entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">collabname</tag>, <tag dbk:condition="v4">corpauthor</tag>,
+<tag dbk:condition="v4">corpcredit</tag>, <tag dbk:condition="v4">corpname</tag></entry>
+<entry><tag>orgname</tag></entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">isbn</tag>, <tag dbk:condition="v4">issn</tag>,
+<tag dbk:condition="v4">pubsnumber</tag></entry>
+<entry><tag>biblioid</tag></entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">lot</tag>, <tag dbk:condition="v4">lotentry</tag>, <tag dbk:condition="v4">tocback</tag>,
+<tag dbk:condition="v4">tocchap</tag>, <tag dbk:condition="v4">tocfront</tag>, <tag dbk:condition="v4">toclevel1</tag>,
+<tag dbk:condition="v4">toclevel2</tag>, <tag dbk:condition="v4">toclevel3</tag>, <tag dbk:condition="v4">toclevel4</tag>,
+<tag dbk:condition="v4">toclevel5</tag>, <tag dbk:condition="v4">tocpart</tag></entry>
+<entry><tag>tocdiv</tag></entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">graphic</tag>, <tag dbk:condition="v4">graphicco</tag>,
+<tag dbk:condition="v4">inlinegraphic</tag>, <tag dbk:condition="v4">mediaobjectco</tag></entry>
+<entry><tag>mediaobject</tag> and <tag>inlinemediaobject</tag></entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">ulink</tag></entry>
+<entry><tag>link</tag></entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">ackno</tag></entry>
+<entry><tag>acknowledgements</tag></entry>
+</row>
+</tbody>
+</tgroup>
+</table>
+
+</section>
+
+<section xml:id="changes-removed">
+<title>Removed elements</title>
+
+<para>The following elements were removed from DocBook V5.0 without
+direct replacements: <tag dbk:condition="v4">action</tag>, <tag
+dbk:condition="v4">beginpage</tag>, <tag dbk:condition="v4">highlights</tag>,
+<tag dbk:condition="v4">interface</tag>, <tag
+dbk:condition="v4">invpartnumber</tag>, <tag
+dbk:condition="v4">medialabel</tag>, <tag dbk:condition="v4">modespec</tag>,
+<tag dbk:condition="v4">structfield</tag>, <tag
+dbk:condition="v4">structname</tag>.
+If you use one or more of these elements, here are some suggestions
+as to how to re-code them in DocBook V5.0.
+</para>
+
+<table xml:id="t.removed">
+<title>Recommended mapping for removed elements</title>
+<tgroup dbk:cols="2">
+<thead>
+<row>
+<entry>Old name</entry>
+<entry>Recommended mapping</entry>
+</row>
+</thead>
+<tbody>
+<row>
+<entry><tag dbk:condition="v4">action</tag></entry>
+<entry>Use <computeroutput>&lt;<tag>phrase</tag> remap="action"&gt;</computeroutput>.</entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">beginpage</tag></entry>
+<entry>Remove: <tag dbk:condition="v4">beginpage</tag> is advisory only
+and has tended to cause confusion.  A processing instruction or
+comment should be a workable replacement if one is needed.</entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">highlights</tag></entry>
+<entry>Use <tag>abstract</tag>.  Note that because <tag
+dbk:condition="v4">highlights</tag> has a broader content model, you may
+need to wrap contents in a <tag>para</tag> inside
+<tag>abstract</tag>.</entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">interface</tag></entry>
+<entry>Use one of the <quote>gui*</quote> elements
+(<tag>guibutton</tag>, <tag>guiicon</tag>, <tag>guilabel</tag>,
+<tag>guimenu</tag>, <tag>guimenuitem</tag>, or
+<tag>guisubmenu</tag>).</entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">invpartnumber</tag></entry>
+<entry>Use <computeroutput>&lt;<tag>biblioid</tag> class="other"
+otherclass="medialabel"&gt;</computeroutput>.  The
+<tag>productnumber</tag> element is another alternative.</entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">medialabel</tag></entry>
+<entry>Use <computeroutput>&lt;<tag>citetitle</tag>
+pubwork="<replaceable>mediatype</replaceable>"&gt;</computeroutput>,
+where <replaceable>mediatype</replaceable> is the type of media being
+labeled (e.g.,<tag dbk:class="attvalue">cdrom</tag> or <tag
+dbk:class="attvalue">dvd</tag>).</entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">modespec</tag></entry>
+<entry>No longer needed.  The current processing model for
+<tag>olink</tag> renders <tag dbk:condition="v4">modespec</tag>
+unnecessary.</entry>
+</row>
+<row>
+<entry><tag dbk:condition="v4">structfield</tag>, <tag dbk:condition="v4">structname</tag></entry>
+<entry>Use <tag>varname</tag>. If you need to distinguish between the
+two, use <computeroutput>&lt;<tag>varname</tag>
+remap="<replaceable>structname or
+structfield</replaceable>"&gt;</computeroutput>.  In some contexts, it
+may also be appropriate to use <tag>property</tag> for <tag
+dbk:condition="v4">structfield</tag>.</entry>
+</row>
+</tbody>
+</tgroup>
+</table>
+
+</section>
+
+</section>
+
+<section xml:id="convert4to5">
+<title>Converting DocBook V4.x documents to DocBook V5.0</title>
+
+<para>The DocBook V5.0 schema ships with an XSLT 1.0 stylesheet that
+is designed to transform valid DocBook V4.x documents to valid
+DocBook V5.0 documents.</para>
+
+<para>To convert your document, <filename>doc.xml</filename> in the
+examples below, follow these steps:</para>
+
+<procedure>
+<step>
+<para>Check the validity of your DocBook XML V4.x document. The
+conversion tool assumes that the input document is valid. If the input
+document contains markup errors, the results will be unpredictable at
+best.</para>
+</step>
+<step>
+<para>Transform <filename>doc.xml</filename> to
+<filename>newdoc.xml</filename> with the
+<filename>db4-upgrade.xsl</filename> stylesheet included in the
+DocBook V5.0 distribution that you are using.</para>
+</step>
+<step>
+<para>Check the validity of your DocBook XML V5.0 document against
+the DocBook V5.0 RELAX NG grammar.</para>
+</step>
+</procedure>
+
+<para>In the vast majority of cases, the resulting document should
+be valid and your conversion process is finished.</para>
+
+<para>If the document is not valid, please report the problem.
+(Over time, we'll have more experience with the sorts of things
+that can go wrong and we'll update this document to reflect that
+experience.)</para>
+
+<section xml:id="entities">
+<title>What About Entities?</title>
+
+<para>Using XSLT to transform existing documents to DocBook V5.0 has
+one potential disadvantage: it removes all entity references from 
+your document.</para>
+
+<para>If preserving entities is an important aspect of your production
+work flow, you will have to engage in a semi-manual process to
+preserve them.</para>
+
+<procedure>
+<step>
+<para>Open your existing document using your favorite editing tool.
+You must use a tool that <emphasis>is not</emphasis> XML-aware, or one
+that allows you to edit markup “in the raw”.</para>
+</step>
+<step>
+<para>Replace all occurrences of the entity references that you want
+to preserve with some unique string. For example, if you want to preserve
+“<literal>&amp;Product;</literal>” references, you could replace them
+all with “<literal>[[[Product]]]</literal>” (assuming that the string
+“<literal>[[[Product]]]</literal>” doesn't occur anywhere else in your document).</para>
+</step>
+<step>
+<para>Copy the document type declaration off of your document and save
+it some place. The document type declaration is everything from
+“<literal>&lt;!DOCTYPE</literal>” to the closing “<literal>]></literal>”.
+</para>
+</step>
+<step>
+<para>Perform the conversion described in <xref dbk:linkend="convert4to5"/>.
+</para>
+</step>
+<step>
+<para>Open the new document using your favorite editing tool. Replace
+all occurrences of the unique string you used to save the entity references
+with the corresponding entity references.</para>
+</step>
+<step>
+<para>Paste the document type declaration that you saved onto the top
+of your new document.</para>
+</step>
+<step>
+<para>Remove the external identifier (the <literal>PUBLIC</literal>
+and/or <literal>SYSTEM</literal> keywords) from the document type
+declaration. A document that begins:</para>
+<programlisting><![CDATA[<!DOCTYPE book [
+<!ENTITY someEntity "some replacement text">
+]>]]></programlisting>
+<para>is perfectly well-formed. If you don't remove the references to
+the DTD, then your parser will likely try to validate against DocBook
+V4.0 and that's not going to work. Alternatively, you could refer
+to the DocBook V5.0 DTD.</para>
+</step>
+</procedure>
+
+<tip>
+<para>Steps 2 and 5 from previous procedure can be automated using the
+<link xl:href="http://docbook.svn.sourceforge.net/viewvc/docbook/trunk/contrib/tools/cloak">cloak
+script</link> written by Michael Smith.</para>
+</tip>
+
+<section xml:id="extparsedentities">
+<title>External Parsed Entities</title>
+
+<para>External parsed entities, entities which load part of a document
+from another file, are a special case. These can often be replaced
+with XInclude elements.</para>
+
+<para>The Perl script <filename>db4-entities.pl</filename>, also included
+in the DocBook V5.0 distribution attempts to perform this replacement
+for you. To use the script, perform the following steps:</para>
+
+<procedure>
+<step>
+<para>Process your document with <filename>db4-entities.pl</filename>.
+The script expects a single filename and prints the XInclude version
+on standard output.</para>
+</step>
+<step>
+<para>Process the XInclude version as described in <xref
+dbk:linkend="convert4to5"/>.
+</para>
+</step>
+</procedure>
+</section>
+</section>
+
+</section>
+
+<section xml:id="customizations">
+  <title>Customizing DocBook V5.0</title>
+  <!--
+      ** RNG schema organization
+      ** Removing attributes
+      ** Adding new attributes
+      ** Changing permitted content of attribute
+      ** Removing elements
+      ** Adding new elements
+      ** Customizing content models
+      ** Naming and versioning of DocBook customizations
+  -->
+
+  <para>
+    It's much easier to customize DocBook V5.0 than it was to
+    customize earlier releases.  This is partly because RELAX NG
+    provides better support for modifications than DTDs and partly
+    because the DocBook schema is designed to take full advantage
+    of the capabilities RELAX NG provides.
+    This section describes the organization of the RELAX NG schema for
+    DocBook, methods and examples for adding, removing, and modifying elements
+    and attributes, and conventions for naming and versioning
+    DocBook customizations.
+    It assumes some familiarity with RELAX NG.  If you are unfamiliar
+    with RELAX NG, you can find a tutorial introduction in
+    <citation>RNCTUT</citation>.
+  </para>
+  <section xml:id="relaxngorg">
+    <title>DocBook RELAX NG schema organization</title>
+    <para>
+      The DocBook RELAX NG schema is highly modular, using named
+      patterns extensively.  Every element, attribute, attribute
+      list, and enumeration has its own named pattern.  In addition,
+      there are named patterns for logical combinations of elements
+      and attributes.  These named patterns provide <quote>hooks</quote>
+      into the schema that allow you to do a wide range of customization
+      by simply redefining one or more of the named patterns.
+    </para>
+    <para>
+      An important design characteristic of the schema is that
+      duplication is minimized.  This is done through the use of
+      named patterns for common groupings that can be re-used.
+      For example, the <tag>imagedata</tag> and <tag>videodata</tag>
+      elements each have an <tag dbk:class="attribute">align</tag> attribute
+      that takes the same set of enumerated values.  Rather than
+      repeating those values, a single pattern,
+      <varname>db.halign.enumeration</varname> is referenced by
+      the <varname>db.videodata.align.enumeration</varname>
+      and <varname>db.imagedata.align.enumeration</varname> patterns,
+      which are in turn referenced by the
+      <varname>db.videodata.align.attribute</varname>
+      and <varname>db.imagedata.align.attribute</varname> patterns.
+      While this may seem like overkill, it allows a customizer to modify
+      the allowed enumerations for these two attributes separately or together,
+      or to completely re-define the allowed content of either or both,
+      by redefining one or more of these named patterns.
+    </para>
+    <section xml:id="patternnames"><title>Pattern Names</title>
+    <para>
+      Because named patterns are used extensively, the RELAX NG schema uses
+      several naming conventions.  These are:
+      <itemizedlist dbk:spacing="compact">
+        <listitem>
+          <para>
+            Names have two or more parts, separated by dots <quote>.</quote>
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            The first part of each name is the prefix <quote>db</quote>
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            Each element has a named pattern in the form
+            <varname>db.<replaceable>elementname</replaceable></varname>.
+            Elements that have different content models in different
+            contexts will also have patterns in the form
+            <varname>db.<replaceable>context.elementname</replaceable></varname>.  For example, <varname>db.figure.info</varname>
+            defines the content model for the <tag>info</tag> element
+            when it appears as a child of the <tag>figure</tag> element.
+            <replaceable>Context</replaceable> may have several parts.
+            For example, <varname>db.cals.entrytbl.thead</varname>.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            Most attributes have a named pattern in the form
+            <varname>db.<replaceable>attributename</replaceable>.attribute</varname>.
+            Attributes that have different content models in different
+            contexts will also have patterns in the form
+            <varname>db.<replaceable>context.attributename</replaceable>.attribute</varname>.
+            For example,
+            <varname>db.olink.localinfo.attribute</varname> defines the content
+            model of the <tag dbk:class="attribute">localinfo</tag> attribute when
+            it appears in <tag>olink</tag>.
+            There are a few attributes that do not have individual named
+            patterns.  For example, the effectivity attributes are grouped
+            into <varname>db.effectivity.attributes</varname> and not identified
+            separately.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            Each element has a named pattern for its attribute list in
+            the form
+            <varname>db.<replaceable>elementname</replaceable>.attlist</varname>
+
+            that defines the list of attributes for that element.
+            Elements that have different attribute lists in different
+            contexts will also have patterns in the form
+            <varname>db.<replaceable>context.elementname</replaceable>.attlist</varname>
+            For example, <varname>db.html.table.attlist</varname> defines
+            the attribute list for the html <tag dbk:condition="nolink">table</tag> element and
+            <varname>db.cals.table.attlist</varname> defines the attribute
+            list for a cals <tag dbk:condition="nolink">table</tag> element.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            Each attribute that has enumerated values has a
+            named pattern in the form
+            <varname>db.<replaceable>[context.]attributename</replaceable>.enumeration</varname>.
+            If the enumeration for a particular attribute depends on
+            context, optional context is provided.
+            For example,
+            <varname>db.verbatim.continuation.enumeration</varname> defines
+            the enumeration values for the
+            <tag dbk:class="attribute">continuation</tag> attribute that is used
+            in verbatim contexts like <tag>screen</tag>.
+            Unlike elements and attributes, there is not necessarily a
+            named pattern for enumerated attributes outside their context.
+            For example, there is no <varname>db.class.enumeration</varname>
+            because the <tag dbk:class="attribute">class</tag> attribute has
+            a broad and non-intersecting range of uses.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            There are several different groupings of elements and attributes.
+            Here are the major ones:
+            <variablelist dbk:spacing="compact">
+              <varlistentry>
+                <term>inlines</term>
+                <listitem>
+                  <para>
+                    Combinations of inline elements, for example,
+                    <varname>db.error.inlines</varname>, which contains
+                    <varname>db.errorcode</varname>,
+                    <varname>db.errortext</varname>, etc.
+                  </para>
+                </listitem>
+              </varlistentry>
+              <varlistentry>
+                <term>blocks</term>
+                <listitem>
+                  <para>
+                    Combinations of block elements, for example,
+                    <varname>db.verbatim.blocks</varname>, which contains
+                    <varname>db.programlisting</varname>,
+                    <varname>db.screen</varname>, etc.
+                  </para>
+                </listitem>
+              </varlistentry>
+              <varlistentry>
+                <term>attributes</term>
+                <listitem>
+                  <para>
+                    Combinations of attributes, for example,
+                    <varname>db.effectivity.attributes</varname>,
+                    which contains the attributes
+                    <tag dbk:class="attribute">arch</tag>,
+                    <tag dbk:class="attribute">condition</tag>,
+                    <tag dbk:class="attribute">conformance</tag>, etc.
+                  </para>
+                </listitem>
+              </varlistentry>
+              <varlistentry>
+                <term>components</term>
+                <listitem>
+                  <para>
+                    High level components of the schema, for example,
+                    <varname>db.navigation.components</varname>, which contains
+                    <varname>db.glossary</varname>,
+                    <varname>db.bibliography</varname>,
+                    <varname>db.index</varname>, and
+                    <varname>db.toc</varname>, and is used inside the
+                    content model for <tag>chapter</tag>, <tag>appendix</tag>,
+                    and <tag>preface</tag>.
+                  </para>
+                </listitem>
+              </varlistentry>
+              <varlistentry>
+                <term>contentmodel</term>
+                <listitem>
+                  <para>
+                    Shared content models, for example,
+                    <varname>db.admonition.contentmodel</varname>, which contains
+                    the content model for <tag>tip</tag>, <tag>warning</tag>,
+                    <tag>note</tag>, etc.
+                  </para>
+                </listitem>
+              </varlistentry>
+            </variablelist>
+          </para>
+          <para>
+            There are a couple of other groupings designed to minimize
+            duplication, but these are the most important.
+          </para>
+        </listitem>
+      </itemizedlist>
+    </para>
+  </section>
+</section>
+<section xml:id="customconsiderations">
+  <title>General customization considerations</title>
+  <para>
+    Creating a customized schema is similar to
+    creating a customization layer for XSL.  The schema customization
+    layer is a new RELAX NG schema that defines your changes and
+    includes the standard docbook schema.  You then validate using
+    the schema customization as your schema.
+  </para>
+  <para>
+    <xref dbk:linkend="ex-empty" dbk:xrefstyle="select: label"/> is an empty
+    RELAX NG customization that does nothing
+    except define the name spaces and include the standard DocBook schema.
+    The <tag dbk:class="attribute">href</tag> attribute of the
+    <tag dbk:condition="nolink">include</tag> element points to
+    the location of the standard DocBook V5.0
+    schema.<footnote><para>The examples in this section use
+    <filename>docbook.rng</filename> as the schema location. If you want
+    to create a portable schema customization you should use a standard
+    web-accessible location like
+    <uri>http://docbook.org/xml/&version;/rng/docbook.rng</uri> and
+    then use <link
+    xl:href="http://www.oasis-open.org/committees/download.php/14809/xml-catalogs.html">XML
+    catalogs</link> to resolve this location to your local copy of the
+    schema for improved performance. Unfortunately, at the time of
+    this writing not all RELAX NG validators support XML catalogs.</para></footnote>
+    All of the examples are given in both RNG and RNC form.
+<example xml:id="ex-empty"><title>Empty customization file</title>
+<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:db="http://docbook.org/ns/docbook"
+         ns="http://docbook.org/ns/docbook"
+         xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="docbook.rng"/>
+
+  <!-- redefinitions of named patterns -->
+
+</grammar>]]></programlisting>
+<programlisting dbk:language="rnc"><![CDATA[namespace db = "http://docbook.org/ns/docbook"
+
+include "docbook.rnc" inherit = db
+# redefinitions of named patterns]]></programlisting>
+</example>
+  </para>
+</section>
+  <section xml:id="cust-elements">
+    <title>Elements</title>
+    <section xml:id="cust-add-elements">
+      <title>Adding elements</title>
+      <para>
+        Adding an element typically takes two definitions.
+        The first defines the new element and
+        its content model, and the second adds the
+        new element into the schema.  We'll show two examples.
+      </para>
+      <para>
+        <xref dbk:linkend="ex-add-element-1"  dbk:xrefstyle="select: label"/>
+        adds a new element,
+        <tag dbk:condition="nolink">person</tag>, with the same
+        content model as <tag>author</tag>.  The new element will be
+        allowed to appear wherever <tag>author</tag> can appear.
+      </para>
+      <para>
+        The <varname>db.author</varname> pattern is copied
+        and renamed <varname>dbx.person</varname>, defining
+        a new element called <tag dbk:condition="nolink">person</tag>.
+        Then, the <varname>db.author</varname> pattern is redefined
+        to be a choice of the current value or <varname>dbx.person</varname>.
+        The <tag dbk:class="attribute">combine</tag> attribute tells
+        RELAX NG to combine this pattern with the existing named
+        pattern.  In this case, the value
+        of the <tag dbk:class="attribute">combine</tag> attribute is
+        <quote>choice</quote>, which tells the parser that either
+        the original pattern or this new pattern is a valid match.
+      </para>
+<example xml:id="ex-add-element-1"><title>Adding a new element by duplicating an existing one</title>
+<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:db="http://docbook.org/ns/docbook"
+         ns="http://docbook.org/ns/docbook"
+         xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="docbook.rng"/>
+  <!-- define the new element -->
+  <define name="dbx.person">
+    <element name="person">
+        <ref name="db.author.attlist"/>
+        <ref name="db.credit.contentmodel"/>
+    </element>
+  </define>
+  <!-- redefine the db.author pattern to allow db.person in
+       the same places as db.author -->
+  <define name="db.author" combine="choice">
+    <ref name="dbx.person"/>
+  </define>
+</grammar>]]></programlisting>
+<programlisting dbk:language="rnc"><![CDATA[default namespace db = "http://docbook.org/ns/docbook"
+
+include "docbook.rnc"
+# define the new element
+dbx.person =
+  element person { db.author.attlist, db.credit.contentmodel }
+# redefine the db.author pattern to allow db.person in
+# the same places as db.author
+db.author |= dbx.person]]></programlisting>
+</example>
+    <para>
+      The preceding method works well when you'd like a new element
+      to be a clone or near-clone of an existing element.  It gives
+      you complete control over the content model, but
+      only limited control over where the element is allowed.  It
+      works well when you want to allow the element in the same places
+      as an existing element, and for this example that works
+      nicely, since <tag>author</tag> is allowed in four different
+      named patterns, each of which would have had to be redefined to
+      allow <tag dbk:condition="nolink">person</tag>.
+      But, if you can't find an existing element that is allowed in
+      exactly the places you need, this method doesn't work as well.
+    </para>
+    <para>
+      <xref dbk:linkend="ex-add-element-2" dbk:xrefstyle="select: label"/>
+      adds two new elements by combining them into
+      a higher level pattern.  In this example, we'll add
+      two new inline elements for writing about assembly language,
+      <tag dbk:condition="nolink">register</tag> and 
+      <tag dbk:condition="nolink">instruction</tag>.
+      We will allow them wherever programming inlines
+      or operating system inlines are allowed.
+      <xref dbk:linkend="ex-add-element-2" dbk:xrefstyle="select: label"/>
+      defines the two elements, creates a new named pattern
+      (<varname>dbx.asm.inlines</varname>) that contains them, and adds
+      that pattern to <varname>db.programming.inlines</varname> and
+      <varname>db.os.inlines</varname>.  Since these two patterns
+      don't have any elements in common, the strategy used in 
+      <xref dbk:linkend="ex-add-element-1" dbk:xrefstyle="select: label"/>
+      would require selecting two different elements to <quote>clone</quote>,
+      which would be messy.
+    </para>
+<example xml:id="ex-add-element-2"><title>Adding new inline elements</title>
+<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:db="http://docbook.org/ns/docbook"
+         ns="http://docbook.org/ns/docbook"
+         xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="docbook.rng"/>
+  <!-- define the new elements -->
+  <define name="dbx.register">
+    <element name="register">
+      <text/>
+    </element>
+  </define>
+  <define name="dbx.instruction">
+    <element name="instruction">
+      <text/>
+    </element>
+  </define>
+  <!-- create a new pattern that contains the new inlines -->
+  <define name="dbx.asm.inlines">
+    <choice>
+      <ref name="dbx.register"/>
+      <ref name="dbx.instruction"/>
+    </choice>
+  </define>
+  <!-- add the new inlines to programming and os inlines -->
+    <define name="db.programming.inlines" combine="choice">
+      <ref name="dbx.asm.inlines"/>
+    </define>
+    <define name="db.os.inlines" combine="choice">
+      <ref name="dbx.asm.inlines"/>
+    </define>
+</grammar>]]></programlisting>
+<programlisting dbk:language="rnc"><![CDATA[default namespace db = "http://docbook.org/ns/docbook"
+
+include "docbook.rnc"
+# define the new elements
+dbx.register = element register { text }
+dbx.instruction = element instruction { text }
+# create a new pattern that contains the new inlines
+dbx.asm.inlines = dbx.register | dbx.instruction
+# add the new inlines to programming and os inlines
+db.programming.inlines |= dbx.asm.inlines
+db.os.inlines |= dbx.asm.inlines]]></programlisting>
+</example>
+    </section>
+    <section xml:id="cust-delete-elements">
+      <title>Deleting elements</title>
+      <para>
+        Deleting elements is straightforward, but takes some
+        care and planning.  <xref dbk:linkend="ex-delete-element"
+        dbk:xrefstyle="select: label"/> deletes
+        the <tag>important</tag> admonition element by redefining
+        it with a content model of <varname>notAllowed</varname>.
+        Note that in this example, the redefinition is inside
+        the <tag dbk:condition="nolink">include</tag> element.
+        This is required for
+        redefinitions that completely replace an existing pattern.
+      </para>
+      <para>
+        Be careful; If you delete an element that is a required part
+        of another element's content model, you can make it
+        impossible to create a valid document.
+        For example, if you delete the <tag>title</tag>
+        element, you won't be able to validate a <tag>book</tag>
+        because a <tag>book</tag> requires a <tag>title</tag>.
+      </para>
+<example xml:id="ex-delete-element"><title>Deleting an element</title>
+<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:db="http://docbook.org/ns/docbook"
+         ns="http://docbook.org/ns/docbook"
+         xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="docbook.rng">
+    <!-- redefine important element as notAllowed -->
+    <define name="db.important">
+      <notAllowed/>
+    </define>
+  </include>
+</grammar>]]></programlisting>
+<programlisting dbk:language="rnc"><![CDATA[namespace db = "http://docbook.org/ns/docbook"
+
+include "docbook.rnc" inherit = db {
+  # redefine important element as notAllowed
+  db.important = notAllowed
+}]]></programlisting>
+</example>
+    </section>
+    <section xml:id="cust-modify-elements">
+      <title>Customizing the content model of existing elements</title>
+      <para>
+         <xref dbk:linkend="ex-modify-element" dbk:xrefstyle="select: label"/>
+         expands the definition of <tag>author</tag> to include two
+         new elements, <tag dbk:condition="nolink">born</tag> and
+         <tag dbk:condition="nolink">died</tag>.
+         The <tag>author</tag> element allows two content models,
+         <varname>db.person.author.contentmodel</varname>, which
+         defines an author who is a person, and
+         <varname>db.org.author.contentmodel</varname>, which
+         defines an author that is an organization.  We will modify
+         <varname>db.person.author.contentmodel</varname> so that
+         only authors who are persons can have the new elements.
+<example xml:id="ex-modify-element"><title>Modifying the content model of an element</title>
+<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:db="http://docbook.org/ns/docbook"
+         ns="http://docbook.org/ns/docbook"
+         xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="docbook.rng"/>
+
+  <define name="db.person.author.contentmodel" combine="interleave">
+    <interleave>
+      <optional>
+        <element name="born">
+          <ref name="db.date.contentmodel"/>
+        </element>
+      </optional>
+      <optional>
+        <element name="died">
+          <ref name="db.date.contentmodel"/>
+        </element>
+      </optional>
+    </interleave>
+  </define>
+</grammar>]]></programlisting>
+<programlisting dbk:language="rnc"><![CDATA[default namespace = "http://docbook.org/ns/docbook"
+namespace db = "http://docbook.org/ns/docbook"
+
+include "docbook.rnc"
+
+db.person.author.contentmodel &=
+  element born { db.date.contentmodel }?
+  & element died { db.date.contentmodel }?]]></programlisting>
+</example>
+      </para>
+      <para>
+        This modification will allow instances like this:
+<programlisting><![CDATA[<author>
+  <personname>Babe Ruth</personname>
+  <born>02/06/1895</born>
+  <died>08/16/1948</died>
+</author>]]></programlisting>
+but because we only modified the content model for authors
+who are human, it won't allow an instance like this, which
+uses <varname>db.org.author.contentmodel</varname>:
+<programlisting><![CDATA[<!-- INVALID -->
+<author>
+  <orgname>Boston Red Sox</orgname>
+  <died>1919</died>
+  <born>2004</born>
+</author>]]></programlisting>
+      </para>
+    </section>
+  </section>
+  <section xml:id="cust-attributes">
+    <title>Attributes</title>
+    <section xml:id="cust-add-attributes">
+      <title>Adding attributes</title>
+      <para>
+        The simplest way to add an attribute to a single element
+        is to add it to the attlist pattern for that element.
+        <xref dbk:linkend="ex-add-attr" dbk:xrefstyle="select: label"/>
+        adds the optional attributes <tag dbk:class="attribute">born</tag>
+        and <tag dbk:class="attribute">died</tag> to the attribute
+        list for <tag>author</tag>.
+        The <varname>db.author.attlist</varname>
+        named pattern is redefined with the
+        <tag dbk:class="attribute">combine</tag> attribute set to
+        <quote>interleave</quote>, which interleaves the two new
+        optional attributes with the existing attributes on the list.
+      </para>
+<example xml:id="ex-add-attr"><title>Adding attributes</title>
+<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:db="http://docbook.org/ns/docbook"
+         ns="http://docbook.org/ns/docbook"
+         xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="docbook.rng"/>
+
+  <define name="db.author.attlist" combine="interleave">
+    <interleave>
+      <optional>
+        <attribute name="born">
+          <ref name="db.date.contentmodel"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="died">
+          <ref name="db.date.contentmodel"/>
+        </attribute>
+      </optional>
+    </interleave>
+  </define>
+</grammar>]]></programlisting>
+<programlisting dbk:language="rnc"><![CDATA[namespace db = "http://docbook.org/ns/docbook"
+
+include "docbook.rnc" inherit = db
+
+db.author.attlist &=
+  attribute born { db.date.contentmodel }?
+  & attribute died { db.date.contentmodel }?]]></programlisting>
+</example>
+    <para>
+      Unlike
+      <xref dbk:linkend="ex-modify-element" dbk:xrefstyle="select: label"/>,
+      <xref dbk:linkend="ex-add-attr" dbk:xrefstyle="select: label"/> allows
+      the new attributes to appear on any <tag>author</tag>
+      element, not just those using the person content model.
+    </para>
+    <para>
+      <xref dbk:linkend="ex-add-attr-2" dbk:xrefstyle="select: label"/> shows
+      how you could limit the use of these attributes to authors who
+      are persons.  In this example, the new attributes are interleaved
+      with the <varname>db.person.author.contentmodel</varname>.  
+      The only difference between this example and 
+      <xref dbk:linkend="ex-modify-element" dbk:xrefstyle="select: label"/> is
+      that the added patterns are identified as attributes rather than
+      elements.  This shows some of the flexibility of RELAX NG, which
+      treats attributes and elements very consistently.
+<example xml:id="ex-add-attr-2"><title>Adding attributes; alternate method</title>
+<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:db="http://docbook.org/ns/docbook"
+         ns="http://docbook.org/ns/docbook"
+         xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="docbook.rng"/>
+  <!-- redefinitions of named patterns -->
+  <define name="db.person.author.contentmodel" combine="interleave">
+    <interleave>
+      <optional>
+        <attribute name="born">
+          <ref name="db.date.contentmodel"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="died">
+          <ref name="db.date.contentmodel"/>
+        </attribute>
+      </optional>
+    </interleave>
+  </define>
+</grammar>]]></programlisting>
+<programlisting dbk:language="rnc"><![CDATA[namespace db = "http://docbook.org/ns/docbook"
+
+include "docbook.rnc" inherit = db
+# redefinitions of named patterns
+db.person.author.contentmodel &=
+  attribute born { db.date.contentmodel }?
+  & attribute died { db.date.contentmodel }?]]></programlisting>
+</example>
+There is one difference in the treatment of attributes and elements
+that is worth noting.  By the XML 1.0 definition, the relative order
+of attributes is not significant.  Therefore, the
+<tag dbk:condition="nolink">interleave</tag> block is not required for
+attributes, though it does no harm.  
+    </para>
+    </section>
+    <section xml:id="cust-delete-attributes">
+      <title>Deleting attributes</title>
+      <para>
+        Deleting an attribute is similar to deleting an element,
+        except that you use the RELAX NG <varname>empty</varname>
+        pattern rather than <varname>notAllowed</varname>.
+        <xref dbk:linkend="ex-delete-attr" dbk:xrefstyle="select: label"/>
+        deletes the linking attributes, which are collected in the
+        <varname>db.common.linking.attributes</varname> pattern,
+        by defining that pattern as <varname>empty</varname>.
+      </para>
+<example xml:id="ex-delete-attr"><title>Deleting an attribute</title>
+<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:db="http://docbook.org/ns/docbook"
+         ns="http://docbook.org/ns/docbook"
+         xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="docbook.rng">
+    <define name="db.common.linking.attributes">
+      <empty/>
+    </define>
+  </include>
+</grammar>]]></programlisting>
+<programlisting dbk:language="rnc"><![CDATA[namespace db = "http://docbook.org/ns/docbook"
+
+include "docbook.rnc" inherit = db {
+  db.common.linking.attributes = empty
+}]]></programlisting>
+</example>
+      <para>
+        Generally, <varname>empty</varname> is used when deleting
+        attributes and <varname>notAllowed</varname> is used when
+        deleting elements.
+      </para>
+    </section>
+    <section xml:id="cust-modify-attributes">
+      <title>Changing permitted content of attributes</title>
+      <para>
+        <xref dbk:linkend="ex-modify-attr" dbk:xrefstyle="select: label"/>
+        modifies <varname>db.spacing.enumeration</varname> to
+        add the additional value <quote>large</quote>.  Note
+        that to remove a value from an enumeration, you need
+        to redefine the entire enumeration, minus the values
+        you don't need.
+      </para>
+<example xml:id="ex-modify-attr"><title>Deleting an attribute</title>
+<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:db="http://docbook.org/ns/docbook"
+         ns="http://docbook.org/ns/docbook"
+         xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="docbook.rng"/>
+  <!-- add value to an enumeration -->
+  <define name="db.spacing.enumeration" combine="choice">
+    <value>large</value>
+  </define>
+</grammar>]]></programlisting>
+<programlisting dbk:language="rnc"><![CDATA[namespace db = "http://docbook.org/ns/docbook"
+
+include "docbook.rnc" inherit = db
+# add value to an enumeration
+db.spacing.enumeration |= "large"]]></programlisting>
+</example>
+    </section>
+  </section>
+
+<section xml:id="cust-naming">
+<title>Naming and versioning DocBook customizations</title>
+
+<para>DocBook V5.0 is not tightly coupled with some particular
+validation technology like DTDs. This also means that DocBook V5.0
+documents don't have to (and usually don't) start with a
+document type declaration (&lt;!DOCTYPE…>) to specify the schema
+(DTD) to use. Instead, DocBook V5.0 instances can be easily
+distinguished from other XML vocabularies by using elements in the
+<uri>http://docbook.org/ns/docbook</uri> namespace. This namespace is
+enough to distinguish DocBook from other XML based formats. But the
+DocBook schema evolves over time and there are several versions of
+DocBook (e.g. 3.1, 4.2, 4.5 and 5.0).  Since DocBook version 5.0, the
+actual version used is indicated in the <tag
+dbk:class="attribute">version</tag> attribute on a root element.</para>
+
+<programlisting><![CDATA[<book xmlns="http://docbook.org/ns/docbook"
+      version="5.0">
+  …
+</book>]]></programlisting>
+
+<para>Future versions of DocBook documents will start with the same
+markup, except the version number will be raised, for example to 5.1
+or 6.0.
+The namespace will remain the same until the semantics of the elements
+change in a backward incompatible way, which is very unlikely to happen.</para>
+
+<para>If you create a DocBook schema customization you must change the <tag
+dbk:class="attribute">version</tag> attribute to distinguish your
+customization from the <quote>official</quote> DocBook.  Changing the
+namespace is not recommended because that would break the processing
+tools.  Remember that changing namespaces is the same as renaming all
+elements in the namespace.</para>
+
+<para>When you customize the schema, use the following syntax to
+identify your DocBook derivation:</para>
+
+<programlisting><replaceable>base_version</replaceable>-[subset|extension|variant] [<replaceable>name</replaceable>[-<replaceable>version</replaceable>]?]+</programlisting>
+
+<para>For example:</para>
+
+<programlisting>5.0-subset simplified-1.0
+5.0-variant ASMBook
+5.0-variant ASMBook-2006
+5.0-extension MathML-2.0 SVG-1.1</programlisting>
+
+<para>The first part of the version identifier is the version number of the
+DocBook schema from which you derived your customization.</para>
+
+<para>If your schema is a proper subset, you can advertise this status
+by using the <literal>subset</literal> keyword in the description. If
+your schema contains any markup model extensions, you can advertise
+this status by using the <literal>extension</literal> keyword. If
+you'd rather not characterize your variant specifically as a subset or
+an extension, use the <literal>variant</literal> keyword.</para>
+
+<para>After these keywords you may add a whitespace separated list of
+customization identifiers. Each name may be optionally followed by its
+version number.</para>
+
+</section>
+
+</section>
+
+<section xml:id="faq">
+<title>FAQ</title>
+
+<qandaset>
+<qandadiv>
+<title>Authoring</title>
+
+<qandaentry xml:id="faq-authoring-schema-association">
+<question>
+<para>How do I attach a schema to a DocBook V5.0 document when I do not
+want to use DTDs and !DOCTYPE?</para>
+</question>
+<answer>
+<para>There is no standard way of associating a RELAX NG schema with a
+document. Most tools provide some mechanism for performing this
+association, consult the documentation for your application. In some
+tools you must specify schema manually each time you want to
+edit/process your document.</para>
+</answer>
+</qandaentry>
+
+<qandaentry xml:id="faq-authoring-general-entities">
+<question>
+<para>How do I use entities like <tag dbk:class="genentity">ndash</tag> in
+DocBook V5.0?</para>
+</question>
+<answer>
+<para>Modern schema languages (including RELAX NG and W3X XML Schema)
+do not provide any means to define entities that can be used for easier
+typing of special characters. Some editors provide functions or
+special toolbars that allow you to easily pick necessary character
+and insert it into document as a raw Unicode character or a numeric
+character reference.</para>
+<para>Another possibility is to include entity definitions in the
+prolog of your document. <link
+xl:href="http://www.w3.org/2003/entities/">Entity definition
+files</link> are now maintained by W3C. You can reference definition
+files with entity definitions you are interested in and then reference
+imported entities. For example:</para>
+<programlisting><![CDATA[<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE article [
+<!ENTITY % isopub SYSTEM "http://www.w3.org/2003/entities/iso8879/isopub.ent">
+%isopub;
+]>
+<article xmlns="http://docbook.org/ns/docbook" version="5.0">
+<title>DocBook V5.0 &ndash; the superb documentation format</title>]]>
+…</programlisting>
+<para>For your convenience there is also flattened entity definition
+file which contains all entity definitions.</para>
+<programlisting><![CDATA[<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE article [
+<!ENTITY % allent SYSTEM "http://www.w3.org/2003/entities/2007/w3centities-f.ent">
+%allent;
+]>
+<article xmlns="http://docbook.org/ns/docbook" version="5.0">
+<title>DocBook V5.0 &ndash; the superb documentation format</title>]]>
+…</programlisting>
+</answer>
+</qandaentry>
+
+<qandaentry xml:id="faq-authoring-modularization">
+<question>
+<para>How to modularize documents?</para>
+</question>
+<answer>
+<para>You can use <link
+xl:href="http://www.w3.org/TR/xinclude/">XInclude</link> for this
+task. There is an alternative schema for DocBook V5.0 that
+contains XInclude elements. This is necessary to make some XML editors
+happy. This schema can be found in files that end with letters <quote>xi</quote>, e.g.
+<filename>docbookxi.rnc</filename> instead of
+<filename>docbook.rnc</filename>.</para>
+</answer>
+</qandaentry>
+
+<qandaentry xml:id="faq-authoring-validating-xincludes">
+<question>
+<para>How to validate documents which are composed by XInclude?</para>
+</question>
+<answer>
+<para>If you are using XIncludes you should make sure that the final
+document after resolving all inclusions is valid DocBook V5.0
+instance. This means that all XIncludes should be processed before
+validation takes place. The following command can be used to enable
+XInclude processing in oNVDL.</para>
+<screen><command>java</command> -Dorg.apache.xerces.xni.parser.XMLParserConfiguration=org.apache.xerces.parsers.XIncludeParserConfiguration -jar <replaceable>/path/to/oNVDL/</replaceable>bin/onvdl.jar <replaceable>/path/to/</replaceable>docbook.nvdl document.xml</screen>
+<para>For JNVDL you can use switch <option>-xi</option> to enable XInclude processing.</para>
+</answer>
+</qandaentry>
+
+</qandadiv>
+
+<qandadiv>
+<title>Stylesheets</title>
+
+<qandaentry xml:id="faq-stylesheets-future">
+<question>
+<para>Will the current DocBook XSL stylesheets (XSLT 1.0 based
+implementation) be maintained and improved in the future since work on
+a new XSLT 2.0 based implementation has started?</para>
+</question>
+<answer>
+<para>Yes, the current stylesheets (like 1.73.x) will be supported and
+improved further because they are very widely deployed and work with
+many existing XSLT processors.</para>
+<para>Surely there will be a point in a future when all new development
+will be switched to the XSLT 2.0 based implementation. But this
+will not happen until all features of the current stylesheets are
+implemented in the new stylesheets, and until there is more than
+one usable XSLT 2.0 processor available.</para>
+</answer>
+</qandaentry>
+
+</qandadiv>
+
+<qandadiv>
+<title>Schema customizations</title>
+
+<qandaentry xml:id="faq-customization-mathml">
+<question>
+<para>How can I extend the DocBook schema with MathML elements?</para>
+</question>
+<answer>
+<para>The basic DocBook schema allows elements from the MathML namespace
+to appear inside the <tag>equation</tag> element.  This means that you can
+validate a DocBook+MathML document, but MathML content will be ignored
+during the validation. You will also not be able to use guided editing
+for the MathML content.</para>
+<para>If you need strict validation of MathML content or guided
+editing for MathML, you can easily extend the base DocBook schema with
+the MathML schema.</para>
+<procedure>
+<title>Extending the DocBook schema with the MathML schema</title>
+<step>
+<para>Download the MathML RELAX NG schema from <link
+xl:href="http://yupotan.sppd.ne.jp/relax-ng/mml2.html"/> and unpack it
+somewhere (e.g. into a <filename>mathml</filename> subdirectory).</para>
+</step>
+<step>
+<para>Create a schema customization in compact syntax—<filename>dbmathml.rnc</filename>:</para>
+<programlisting dbk:language="rnc">namespace html = "http://www.w3.org/1999/xhtml"
+namespace mml = "http://www.w3.org/1998/Math/MathML"
+namespace db = "http://docbook.org/ns/docbook"
+
+include "/path/to/docbook.rnc" {
+  db._any.mml = external "mathml/mathml2.rnc"
+  db._any =
+    element * - (db:* | html:* | mml:*) {
+      (attribute * { text }
+       | text
+       | db._any)*
+    }
+}</programlisting>
+<para>Or, alternatively, you can use the XML syntax of RELAX NG—<filename>dbmathml.rng</filename>:</para>
+<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+
+<include href="/path/to/docbook.rng">
+  <define name="db._any.mml">
+    <externalRef href="mathml/mathml2.rng"/>
+  </define>
+
+  <define name="db._any">
+    <element>
+      <anyName>
+        <except>
+          <nsName ns="http://docbook.org/ns/docbook"/>
+          <nsName ns="http://www.w3.org/1999/xhtml"/>
+          <nsName ns="http://www.w3.org/1998/Math/MathML"/>
+        </except>
+      </anyName>
+      <zeroOrMore>
+        <choice>
+          <attribute>
+            <anyName/>
+          </attribute>
+          <text/>
+          <ref name="db._any"/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </define>
+</include>
+
+</grammar>]]></programlisting>
+</step>
+<step>
+<para>Now use the customized schema (<filename>dbmathml.rnc</filename>
+or <filename>dbmathml.rng</filename>) instead of the original
+DocBook schema.</para>
+</step>
+</procedure>
+</answer>
+</qandaentry>
+
+<qandaentry xml:id="faq-customization-svg">
+<question>
+<para>How can I extend the DocBook schema with SVG elements?</para>
+</question>
+<answer>
+<para>The situation is the same as with MathML support. You can use
+elements from the SVG namespace inside the <tag>imageobject</tag>
+element.</para>
+<procedure>
+<title>Extending the DocBook schema with the SVG schema</title>
+<step>
+<para>Download the SVG RELAX NG schema from <link
+xl:href="http://www.w3.org/Graphics/SVG/1.1/rng/rng.zip"/> and unpack it
+somewhere (e.g. into an <filename>svg</filename> subdirectory).</para>
+</step>
+<step>
+<para>Create a schema customization in compact syntax—<filename>dbsvg.rnc</filename>:</para>
+<programlisting dbk:language="rnc">namespace html = "http://www.w3.org/1999/xhtml"
+namespace db = "http://docbook.org/ns/docbook"
+namespace svg = "http://www.w3.org/2000/svg"
+
+include "/path/to/docbook.rnc" {
+  db._any.svg = external "svg/svg11.rnc"
+  db._any =
+    element * - (db:* | html:* | svg:*) {
+      (attribute * { text }
+       | text
+       | db._any)*
+    }
+}</programlisting>
+<para>Or, alternatively, you can use the XML syntax of RELAX NG—<filename>dbsvg.rng</filename>:</para>
+<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+
+<include href="/path/to/docbook.rng">
+  <define name="db._any.svg">
+    <externalRef href="svg/svg11.rng"/>
+  </define>
+
+  <define name="db._any">
+    <element>
+      <anyName>
+        <except>
+          <nsName ns="http://docbook.org/ns/docbook"/>
+          <nsName ns="http://www.w3.org/1999/xhtml"/>
+          <nsName ns="http://www.w3.org/2000/svg"/>
+        </except>
+      </anyName>
+      <zeroOrMore>
+        <choice>
+          <attribute>
+            <anyName/>
+          </attribute>
+          <text/>
+          <ref name="db._any"/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </define>
+</include>
+
+</grammar>]]></programlisting>
+</step>
+<step>
+<para>Now use the customized schema (<filename>dbsvg.rnc</filename>
+or <filename>dbsvg.rng</filename>) instead of the original
+DocBook schema.</para>
+</step>
+</procedure>
+</answer>
+</qandaentry>
+
+<qandaentry xml:id="faq-customization-mathml-svg">
+<question>
+<para>Is it possible to use the previous two customizations for MathML
+and SVG together?</para>
+</question>
+<answer>
+<para>Yes, you can create a special schema customization that combines
+both MathML and SVG with the DocBook schema. In compact syntax, the merged
+schema is:</para>
+<programlisting dbk:language="rnc">namespace html = "http://www.w3.org/1999/xhtml"
+namespace mml = "http://www.w3.org/1998/Math/MathML"
+namespace db = "http://docbook.org/ns/docbook"
+namespace svg = "http://www.w3.org/2000/svg"
+
+include "/path/to/docbook.rnc" {
+  db._any.mml = external "mahtml/mathml2.rnc"
+  db._any.svg = external "svg/svg11.rnc"
+  db._any =
+    element * - (db:* | html:* | mml:* | svg:*) {
+      (attribute * { text }
+       | text
+       | db._any)*
+    }
+}</programlisting>
+<para>Or alternatively in the full RELAX NG syntax:</para>
+<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+
+<include href="/path/to/docbook.rng">
+  <define name="db._any.mml">
+    <externalRef href="mathml/mathml2.rng"/>
+  </define>
+
+  <define name="db._any.svg">
+    <externalRef href="svg/svg11.rng"/>
+  </define>
+
+  <define name="db._any">
+    <element>
+      <anyName>
+        <except>
+          <nsName ns="http://docbook.org/ns/docbook"/>
+          <nsName ns="http://www.w3.org/1999/xhtml"/>
+          <nsName ns="http://www.w3.org/1998/Math/MathML"/>
+          <nsName ns="http://www.w3.org/2000/svg"/>
+        </except>
+      </anyName>
+      <zeroOrMore>
+        <choice>
+          <attribute>
+            <anyName/>
+          </attribute>
+          <text/>
+          <ref name="db._any"/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </define>
+</include>
+
+</grammar>]]></programlisting>
+</answer>
+</qandaentry>
+
+<qandaentry xml:id="faq-customization-links">
+<question>
+<para>Are there any other examples of schema customization
+available?</para>
+</question>
+<answer>
+<para>Sure. Some of the are listed bellow:</para>
+<itemizedlist>
+<listitem><para><link
+xl:href="http://www.w3.org/TR/xml-i18n-bp/#docbook-plus-its">Sample
+customization of ITS and DocBook</link></para></listitem>
+<listitem><para><link
+xl:href="http://wiki.docbook.org/topic/DocbookSchemas">Examples on
+DocBook WiKi</link></para></listitem>
+</itemizedlist>
+</answer>
+</qandaentry>
+
+</qandadiv>
+
+<qandadiv>
+<title>Tool specific problems</title>
+
+<qandaentry xml:id="faq-tools-xmlspy-xmlid">
+<question>
+<para>I'm using Altova XMLSpy to validate DocBook V5.0 instances
+against the W3C XML Schema (<filename>docbook.xsd</filename>). XMLSpy
+complains about undefined <tag dbk:class="attribute">xml:id</tag>
+attributes?</para>
+</question>
+<answer>
+<para>XMLSpy always uses its own bundled version of
+<filename>xml.xsd</filename> which unfortunately doesn't define the <tag
+dbk:class="attribute">xml:id</tag> attribute. The bundled version of
+<filename>xml.xsd</filename> is hardwired into the program and cannot
+be replaced by a newer version. To solve this problem you must upgrade
+to version 2006 SP1.</para>
+</answer>
+</qandaentry>
+
+</qandadiv>
+
+</qandaset>
+</section>
+
+<bibliography xml:id="references">
+
+<bibliomixed>
+<abbrev>RNCTUT</abbrev>
+Clark, James – Cowan, John – MURATA, Makoto: <title>RELAX NG Compact Syntax Tutorial</title>.
+Working Draft, 26 March 2003. OASIS. <bibliomisc><link xl:href="http://relaxng.org/compact-tutorial-20030326.html"/></bibliomisc>
+</bibliomixed>
+
+<bibliomixed>
+<abbrev>NVDLTUT</abbrev>
+Nálevka, Petr:
+<title>NVDL Tutorial</title>.
+<bibliomisc><link xl:href="http://jnvdl.sourceforge.net/tutorial.html"/></bibliomisc>
+</bibliomixed>
+
+<bibliomixed>
+<abbrev>XMLID</abbrev>
+Marsh, Jonathan – 
+Veillard, Daniel –
+Walsh, Norman: <title>xml:id Version 1.0</title>. W3C Recommendation, 9 September 2005. <bibliomisc><link xl:href="http://www.w3.org/TR/xml-id/"/></bibliomisc>
+</bibliomixed>
+
+<bibliomixed>
+<abbrev>DB5SPEC</abbrev>
+Norman, Walsh: <title>The DocBook Schema</title>.
+Working Draft 5.0a1, OASIS, 29 June 2005.
+<bibliomisc><link xl:href="http://www.docbook.org/specs/wd-docbook-docbook-5.0a1.html"/></bibliomisc>
+</bibliomixed>
+
+</bibliography>
+</article>
+</book>
diff --git a/org.argeo.jcr/ext/test/org/argeo/jcr/tabular/JcrTabularTest.java b/org.argeo.jcr/ext/test/org/argeo/jcr/tabular/JcrTabularTest.java
new file mode 100644 (file)
index 0000000..8896b2f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr.tabular;
+
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.commons.cnd.CndImporter;
+import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.util.tabular.TabularColumn;
+import org.argeo.util.tabular.TabularRow;
+import org.argeo.util.tabular.TabularRowIterator;
+import org.argeo.util.tabular.TabularWriter;
+
+public class JcrTabularTest extends AbstractJackrabbitTestCase {
+       private final static Log log = LogFactory.getLog(JcrTabularTest.class);
+
+       public void testWriteReadCsv() throws Exception {
+               session().setNamespacePrefix("argeo", ArgeoNames.ARGEO_NAMESPACE);
+               InputStreamReader reader = new InputStreamReader(getClass()
+                               .getResourceAsStream("/org/argeo/jcr/argeo.cnd"));
+               CndImporter.registerNodeTypes(reader, session());
+               reader.close();
+
+               // write
+               Integer columnCount = 15;
+               Long rowCount = 1000l;
+               String stringValue = "test, \ntest";
+
+               List<TabularColumn> header = new ArrayList<TabularColumn>();
+               for (int i = 0; i < columnCount; i++) {
+                       header.add(new TabularColumn("col" + i, PropertyType.STRING));
+               }
+               Node tableNode = session().getRootNode().addNode("table",
+                               ArgeoTypes.ARGEO_TABLE);
+               TabularWriter writer = new JcrTabularWriter(tableNode, header,
+                               ArgeoTypes.ARGEO_CSV);
+               for (int i = 0; i < rowCount; i++) {
+                       List<Object> objs = new ArrayList<Object>();
+                       for (int j = 0; j < columnCount; j++) {
+                               objs.add(stringValue);
+                       }
+                       writer.appendRow(objs.toArray());
+               }
+               writer.close();
+               session().save();
+
+               if (log.isDebugEnabled())
+                       log.debug("Wrote tabular content " + rowCount + " rows, "
+                                       + columnCount + " columns");
+               // read
+               TabularRowIterator rowIt = new JcrTabularRowIterator(tableNode);
+               Long count = 0l;
+               while (rowIt.hasNext()) {
+                       TabularRow tr = rowIt.next();
+                       assertEquals(header.size(), tr.size());
+                       count++;
+               }
+               assertEquals(rowCount, count);
+               if (log.isDebugEnabled())
+                       log.debug("Read tabular content " + rowCount + " rows, "
+                                       + columnCount + " columns");
+       }
+}
diff --git a/org.argeo.jcr/ext/test/org/argeo/server/jcr/JcrResourceAdapterTest.java b/org.argeo.jcr/ext/test/org/argeo/server/jcr/JcrResourceAdapterTest.java
new file mode 100644 (file)
index 0000000..3ce499a
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.server.jcr;
+
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase;
+import org.argeo.jcr.JcrResourceAdapter;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+
+public class JcrResourceAdapterTest extends AbstractJackrabbitTestCase {
+       private static SimpleDateFormat sdf = new SimpleDateFormat(
+                       "yyyyMMdd:hhmmss.SSS");
+
+       private final static Log log = LogFactory
+                       .getLog(JcrResourceAdapterTest.class);
+
+       private JcrResourceAdapter jra;
+
+       public void testCreate() throws Exception {
+               String basePath = "/test/subdir";
+               jra.mkdirs(basePath);
+               Resource res = new ClassPathResource("org/argeo/server/jcr/dummy00.xls");
+               String filePath = basePath + "/dummy.xml";
+               jra.create(filePath, res.getInputStream(), "application/vnd.ms-excel");
+               InputStream in = jra.retrieve(filePath);
+               assertTrue(IOUtils.contentEquals(res.getInputStream(), in));
+       }
+
+       public void testVersioning() throws Exception {
+               String basePath = "/test/versions";
+               jra.mkdirs(basePath);
+               String filePath = basePath + "/dummy.xml";
+               Resource res00 = new ClassPathResource(
+                               "org/argeo/server/jcr/dummy00.xls");
+               jra.create(filePath, res00.getInputStream(), "application/vnd.ms-excel");
+               Resource res01 = new ClassPathResource(
+                               "org/argeo/server/jcr/dummy01.xls");
+               jra.update(filePath, res01.getInputStream());
+               Resource res02 = new ClassPathResource(
+                               "org/argeo/server/jcr/dummy02.xls");
+               jra.update(filePath, res02.getInputStream());
+
+               List<Calendar> versions = jra.listVersions(filePath);
+               log.debug("Versions of " + filePath);
+               int count = 0;
+               for (Calendar version : versions) {
+                       log.debug(" " + (count == 0 ? "base" : count - 1) + "\t"
+                                       + sdf.format(version.getTime()));
+                       count++;
+               }
+
+               assertEquals(4, versions.size());
+
+               InputStream in = jra.retrieve(filePath, 1);
+               assertTrue(IOUtils.contentEquals(res01.getInputStream(), in));
+               in = jra.retrieve(filePath, 0);
+               assertTrue(IOUtils.contentEquals(res00.getInputStream(), in));
+               in = jra.retrieve(filePath, 2);
+               assertTrue(IOUtils.contentEquals(res02.getInputStream(), in));
+               Resource res03 = new ClassPathResource(
+                               "org/argeo/server/jcr/dummy03.xls");
+               jra.update(filePath, res03.getInputStream());
+               in = jra.retrieve(filePath, 1);
+               assertTrue(IOUtils.contentEquals(res01.getInputStream(), in));
+       }
+
+       @Override
+       protected void setUp() throws Exception {
+               log.debug("SET UP");
+               super.setUp();
+               jra = new JcrResourceAdapter();
+               jra.setSession(session());
+       }
+
+       @Override
+       protected void tearDown() throws Exception {
+               log.debug("TEAR DOWN");
+               super.tearDown();
+       }
+}
diff --git a/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy00.xls b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy00.xls
new file mode 100644 (file)
index 0000000..e5846fe
Binary files /dev/null and b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy00.xls differ
diff --git a/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy01.xls b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy01.xls
new file mode 100644 (file)
index 0000000..b5c6b55
Binary files /dev/null and b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy01.xls differ
diff --git a/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy02.xls b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy02.xls
new file mode 100644 (file)
index 0000000..d73bc66
Binary files /dev/null and b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy02.xls differ
diff --git a/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy03.xls b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy03.xls
new file mode 100644 (file)
index 0000000..0759cb9
Binary files /dev/null and b/org.argeo.jcr/ext/test/org/argeo/server/jcr/dummy03.xls differ
diff --git a/org.argeo.jcr/pom.xml b/org.argeo.jcr/pom.xml
new file mode 100644 (file)
index 0000000..0abd44c
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <artifactId>argeo-commons</artifactId>
+               <version>2.1.46-SNAPSHOT</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.jcr</artifactId>
+       <name>Commons JCR</name>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitAdminLoginModule.java b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitAdminLoginModule.java
new file mode 100644 (file)
index 0000000..7e17dbc
--- /dev/null
@@ -0,0 +1,47 @@
+package org.argeo.jackrabbit;
+
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.apache.jackrabbit.core.security.SecurityConstants;
+import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
+
+public class JackrabbitAdminLoginModule implements LoginModule {
+       private Subject subject;
+
+       @Override
+       public void initialize(Subject subject, CallbackHandler callbackHandler,
+                       Map<String, ?> sharedState, Map<String, ?> options) {
+               this.subject = subject;
+       }
+
+       @Override
+       public boolean login() throws LoginException {
+               // TODO check permission?
+               return true;
+       }
+
+       @Override
+       public boolean commit() throws LoginException {
+               subject.getPrincipals().add(
+                               new AdminPrincipal(SecurityConstants.ADMIN_ID));
+               return true;
+       }
+
+       @Override
+       public boolean abort() throws LoginException {
+               return true;
+       }
+
+       @Override
+       public boolean logout() throws LoginException {
+               subject.getPrincipals().removeAll(
+                               subject.getPrincipals(AdminPrincipal.class));
+               return true;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitContainer.java b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitContainer.java
new file mode 100644 (file)
index 0000000..c5ca795
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jackrabbit;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.api.JackrabbitRepository;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
+import org.argeo.jcr.ArgeoJcrException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.JcrUtils;
+import org.springframework.core.io.Resource;
+import org.springframework.util.SystemPropertyUtils;
+import org.xml.sax.InputSource;
+
+/**
+ * Wrapper around a Jackrabbit repository which allows to configure it in Spring
+ * and expose it as a {@link Repository}.
+ */
+@Deprecated
+public class JackrabbitContainer extends JackrabbitWrapper {
+       private final static Log log = LogFactory.getLog(JackrabbitContainer.class);
+
+       // local
+       private Resource configuration;
+
+       private Resource variables;
+
+       private RepositoryConfig repositoryConfig;
+       private File homeDirectory;
+       private Boolean inMemory = false;
+
+       /** Migrations to execute (if not already done) */
+       private Set<JackrabbitDataModelMigration> dataModelMigrations = new HashSet<JackrabbitDataModelMigration>();
+
+       /** Straight (non spring) values */
+       private Properties configurationProperties;
+       private InputSource configurationXml;
+
+       /**
+        * Empty constructor, {@link #init()} should be called after properties have
+        * been set
+        */
+       public JackrabbitContainer() {
+       }
+
+       public void init() {
+               // long begin = System.currentTimeMillis();
+
+               if (getRepository() != null)
+                       throw new ArgeoJcrException("Cannot be used to wrap another repository");
+               Repository repository = createJackrabbitRepository();
+               super.setRepository(repository);
+
+               // migrate if needed
+               migrate();
+
+               // apply new CND files after migration
+               prepareDataModel();
+
+               // double duration = ((double) (System.currentTimeMillis() - begin)) /
+               // 1000;
+               // if (log.isDebugEnabled())
+               // log.debug("Initialized JCR repository wrapper in " + duration
+               // + " s");
+       }
+
+       /** Actually creates the new repository. */
+       protected Repository createJackrabbitRepository() {
+               long begin = System.currentTimeMillis();
+               InputStream configurationIn = null;
+               Repository repository;
+               try {
+                       // temporary
+                       if (inMemory && getHomeDirectory().exists()) {
+                               FileUtils.deleteDirectory(getHomeDirectory());
+                               log.warn("Deleted Jackrabbit home directory " + getHomeDirectory());
+                       }
+
+                       // process configuration file
+                       Properties vars = getConfigurationProperties();
+                       vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE, getHomeDirectory().getCanonicalPath());
+                       InputSource is;
+                       if (configurationXml != null)
+                               is = configurationXml;
+                       else {
+                               configurationIn = readConfiguration();
+                               is = new InputSource(configurationIn);
+                       }
+                       repositoryConfig = RepositoryConfig.create(is, vars);
+
+                       //
+                       // Actual repository creation
+                       //
+                       repository = RepositoryImpl.create(repositoryConfig);
+
+                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+                       if (log.isTraceEnabled())
+                               log.trace("Created Jackrabbit repository in " + duration + " s, home: " + getHomeDirectory());
+
+                       return repository;
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot create Jackrabbit repository " + getHomeDirectory(), e);
+               } finally {
+                       IOUtils.closeQuietly(configurationIn);
+               }
+       }
+
+       /** Lazy init. */
+       protected File getHomeDirectory() {
+               try {
+                       if (homeDirectory == null) {
+                               if (inMemory) {
+                                       homeDirectory = new File(System.getProperty("java.io.tmpdir") + File.separator
+                                                       + System.getProperty("user.name") + File.separator + "jackrabbit-" + UUID.randomUUID());
+                                       homeDirectory.mkdirs();
+                                       // will it work if directory is not empty??
+                                       homeDirectory.deleteOnExit();
+                               }
+                       }
+
+                       return homeDirectory.getCanonicalFile();
+               } catch (IOException e) {
+                       throw new ArgeoJcrException("Cannot get canonical file for " + homeDirectory, e);
+               }
+       }
+
+       /** Executes migrations, if needed. */
+       protected void migrate() {
+               // No migration to perform
+               if (dataModelMigrations.size() == 0)
+                       return;
+
+               Boolean restartAndClearCaches = false;
+
+               // migrate data
+               Session session = null;
+               try {
+                       session = login();
+                       for (JackrabbitDataModelMigration dataModelMigration : new TreeSet<JackrabbitDataModelMigration>(
+                                       dataModelMigrations)) {
+                               if (dataModelMigration.migrate(session)) {
+                                       restartAndClearCaches = true;
+                               }
+                       }
+               } catch (ArgeoJcrException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot migrate", e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+               }
+
+               // restart repository
+               if (restartAndClearCaches) {
+                       Repository repository = getRepository();
+                       if (repository instanceof RepositoryImpl) {
+                               JackrabbitDataModelMigration.clearRepositoryCaches(((RepositoryImpl) repository).getConfig());
+                       }
+                       ((JackrabbitRepository) repository).shutdown();
+                       createJackrabbitRepository();
+               }
+
+               // set data model version
+               try {
+                       session = login();
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot login to migrated repository", e);
+               }
+
+               for (JackrabbitDataModelMigration dataModelMigration : new TreeSet<JackrabbitDataModelMigration>(
+                               dataModelMigrations)) {
+                       try {
+                               if (session.itemExists(dataModelMigration.getDataModelNodePath())) {
+                                       Node dataModelNode = session.getNode(dataModelMigration.getDataModelNodePath());
+                                       dataModelNode.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION,
+                                                       dataModelMigration.getTargetVersion());
+                                       session.save();
+                               }
+                       } catch (Exception e) {
+                               log.error("Cannot set model version", e);
+                       }
+               }
+               JcrUtils.logoutQuietly(session);
+
+       }
+
+       /** Shutdown the repository */
+       public void destroy() throws Exception {
+               Repository repository = getRepository();
+               if (repository != null && repository instanceof RepositoryImpl) {
+                       long begin = System.currentTimeMillis();
+                       ((RepositoryImpl) repository).shutdown();
+                       if (inMemory)
+                               if (getHomeDirectory().exists()) {
+                                       FileUtils.deleteDirectory(getHomeDirectory());
+                                       if (log.isDebugEnabled())
+                                               log.debug("Deleted Jackrabbit home directory " + getHomeDirectory());
+                               }
+                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+                       if (log.isTraceEnabled())
+                               log.trace("Destroyed Jackrabbit repository in " + duration + " s, home: " + getHomeDirectory());
+               }
+               repository = null;
+       }
+
+       public void dispose() {
+               throw new IllegalArgumentException("Call destroy() method instead of dispose()");
+       }
+
+       /*
+        * UTILITIES
+        */
+       /**
+        * Reads the configuration which will initialize a {@link RepositoryConfig}.
+        */
+       protected InputStream readConfiguration() {
+               try {
+                       return configuration != null ? configuration.getInputStream() : null;
+               } catch (IOException e) {
+                       throw new ArgeoJcrException("Cannot read Jackrabbit configuration " + configuration, e);
+               }
+       }
+
+       /**
+        * Reads the variables which will initialize a {@link Properties}. Returns
+        * null by default, to be overridden.
+        * 
+        * @return a new stream or null if no variables available
+        */
+       protected InputStream readVariables() {
+               try {
+                       return variables != null ? variables.getInputStream() : null;
+               } catch (IOException e) {
+                       throw new ArgeoJcrException("Cannot read Jackrabbit variables " + variables, e);
+               }
+       }
+
+       /**
+        * Resolves ${} placeholders in the provided string. Based on system
+        * properties if no map is provided.
+        */
+       protected String resolvePlaceholders(String string, Map<String, String> variables) {
+               return SystemPropertyUtils.resolvePlaceholders(string);
+       }
+
+       /** Generates the properties to use in the configuration. */
+       protected Properties getConfigurationProperties() {
+               if (configurationProperties != null)
+                       return configurationProperties;
+
+               InputStream propsIn = null;
+               Properties vars;
+               try {
+                       vars = new Properties();
+                       propsIn = readVariables();
+                       if (propsIn != null) {
+                               vars.load(propsIn);
+                       }
+                       // resolve system properties
+                       for (Object key : vars.keySet()) {
+                               // TODO: implement a smarter mechanism to resolve nested ${}
+                               String newValue = resolvePlaceholders(vars.getProperty(key.toString()), null);
+                               vars.put(key, newValue);
+                       }
+                       // override with system properties
+                       vars.putAll(System.getProperties());
+
+                       if (log.isTraceEnabled()) {
+                               log.trace("Jackrabbit config variables:");
+                               for (Object key : new TreeSet<Object>(vars.keySet()))
+                                       log.trace(key + "=" + vars.getProperty(key.toString()));
+                       }
+
+               } catch (IOException e) {
+                       throw new ArgeoJcrException("Cannot read configuration properties", e);
+               } finally {
+                       IOUtils.closeQuietly(propsIn);
+               }
+               return vars;
+       }
+
+       /*
+        * FIELDS ACCESS
+        */
+
+       public void setHomeDirectory(File homeDirectory) {
+               this.homeDirectory = homeDirectory;
+       }
+
+       public void setInMemory(Boolean inMemory) {
+               this.inMemory = inMemory;
+       }
+
+       public void setRepository(Repository repository) {
+               throw new ArgeoJcrException("Cannot be used to wrap another repository");
+       }
+
+       public void setDataModelMigrations(Set<JackrabbitDataModelMigration> dataModelMigrations) {
+               this.dataModelMigrations = dataModelMigrations;
+       }
+
+       public void setVariables(Resource variables) {
+               this.variables = variables;
+       }
+
+       public void setConfiguration(Resource configuration) {
+               this.configuration = configuration;
+       }
+
+       public void setConfigurationProperties(Properties configurationProperties) {
+               this.configurationProperties = configurationProperties;
+       }
+
+       public void setConfigurationXml(InputSource configurationXml) {
+               this.configurationXml = configurationXml;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitDataModelMigration.java b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitDataModelMigration.java
new file mode 100644 (file)
index 0000000..53f0e44
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jackrabbit;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import javax.jcr.Node;
+import javax.jcr.Session;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.commons.cnd.CndImporter;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.argeo.jcr.ArgeoJcrException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.JcrCallback;
+import org.argeo.jcr.JcrUtils;
+import org.springframework.core.io.Resource;
+
+/** Migrate the data in a Jackrabbit repository. */
+public class JackrabbitDataModelMigration implements
+               Comparable<JackrabbitDataModelMigration> {
+       private final static Log log = LogFactory
+                       .getLog(JackrabbitDataModelMigration.class);
+
+       private String dataModelNodePath;
+       private String targetVersion;
+       private Resource migrationCnd;
+       private JcrCallback dataModification;
+
+       /**
+        * Expects an already started repository with the old data model to migrate.
+        * Expects to be run with admin rights (Repository.login() will be used).
+        * 
+        * @return true if a migration was performed and the repository needs to be
+        *         restarted and its caches cleared.
+        */
+       public Boolean migrate(Session session) {
+               long begin = System.currentTimeMillis();
+               Reader reader = null;
+               try {
+                       // check if already migrated
+                       if (!session.itemExists(dataModelNodePath)) {
+                               log.warn("Node " + dataModelNodePath
+                                               + " does not exist: nothing to migrate.");
+                               return false;
+                       }
+                       Node dataModelNode = session.getNode(dataModelNodePath);
+                       if (dataModelNode.hasProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION)) {
+                               String currentVersion = dataModelNode.getProperty(
+                                               ArgeoNames.ARGEO_DATA_MODEL_VERSION).getString();
+                               if (compareVersions(currentVersion, targetVersion) >= 0) {
+                                       log.info("Data model at version " + currentVersion
+                                                       + ", no need to migrate.");
+                                       return false;
+                               }
+                       }
+
+                       // apply transitional CND
+                       if (migrationCnd != null) {
+                               reader = new InputStreamReader(migrationCnd.getInputStream());
+                               CndImporter.registerNodeTypes(reader, session, true);
+                               session.save();
+                               log.info("Registered migration node types from " + migrationCnd);
+                       }
+
+                       // modify data
+                       dataModification.execute(session);
+
+                       // apply changes
+                       session.save();
+
+                       long duration = System.currentTimeMillis() - begin;
+                       log.info("Migration of data model " + dataModelNodePath + " to "
+                                       + targetVersion + " performed in " + duration + "ms");
+                       return true;
+               } catch (Exception e) {
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoJcrException("Migration of data model "
+                                       + dataModelNodePath + " to " + targetVersion + " failed.",
+                                       e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+                       IOUtils.closeQuietly(reader);
+               }
+       }
+
+       protected static int compareVersions(String version1, String version2) {
+               // TODO do a proper version analysis and comparison
+               return version1.compareTo(version2);
+       }
+
+       /** To be called on a stopped repository. */
+       public static void clearRepositoryCaches(RepositoryConfig repositoryConfig) {
+               try {
+                       String customeNodeTypesPath = "/nodetypes/custom_nodetypes.xml";
+                       // FIXME causes weird error in Eclipse
+                       //repositoryConfig.getFileSystem().deleteFile(customeNodeTypesPath);
+                       if (log.isDebugEnabled())
+                               log.debug("Cleared " + customeNodeTypesPath);
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot clear caches", e);
+               }
+
+               // File customNodeTypes = new File(home.getPath()
+               // + "/repository/nodetypes/custom_nodetypes.xml");
+               // if (customNodeTypes.exists()) {
+               // customNodeTypes.delete();
+               // if (log.isDebugEnabled())
+               // log.debug("Cleared " + customNodeTypes);
+               // } else {
+               // log.warn("File " + customNodeTypes + " not found.");
+               // }
+       }
+
+       /*
+        * FOR USE IN (SORTED) SETS
+        */
+
+       public int compareTo(JackrabbitDataModelMigration dataModelMigration) {
+               // TODO make ordering smarter
+               if (dataModelNodePath.equals(dataModelMigration.dataModelNodePath))
+                       return compareVersions(targetVersion,
+                                       dataModelMigration.targetVersion);
+               else
+                       return dataModelNodePath
+                                       .compareTo(dataModelMigration.dataModelNodePath);
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (!(obj instanceof JackrabbitDataModelMigration))
+                       return false;
+               JackrabbitDataModelMigration dataModelMigration = (JackrabbitDataModelMigration) obj;
+               return dataModelNodePath.equals(dataModelMigration.dataModelNodePath)
+                               && targetVersion.equals(dataModelMigration.targetVersion);
+       }
+
+       @Override
+       public int hashCode() {
+               return targetVersion.hashCode();
+       }
+
+       public void setDataModelNodePath(String dataModelNodePath) {
+               this.dataModelNodePath = dataModelNodePath;
+       }
+
+       public void setTargetVersion(String targetVersion) {
+               this.targetVersion = targetVersion;
+       }
+
+       public void setMigrationCnd(Resource migrationCnd) {
+               this.migrationCnd = migrationCnd;
+       }
+
+       public void setDataModification(JcrCallback dataModification) {
+               this.dataModification = dataModification;
+       }
+
+       public String getDataModelNodePath() {
+               return dataModelNodePath;
+       }
+
+       public String getTargetVersion() {
+               return targetVersion;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java
new file mode 100644 (file)
index 0000000..e413488
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jackrabbit;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.commons.JcrUtils;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
+import org.apache.jackrabbit.jcr2dav.Jcr2davRepositoryFactory;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.ArgeoJcrException;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.xml.sax.InputSource;
+
+/**
+ * Repository factory which can create new repositories and access remote
+ * Jackrabbit repositories
+ */
+public class JackrabbitRepositoryFactory implements RepositoryFactory, ArgeoJcrConstants {
+       private final static Log log = LogFactory.getLog(JackrabbitRepositoryFactory.class);
+
+       private Resource fileRepositoryConfiguration = new ClassPathResource("/org/argeo/jackrabbit/repository-h2.xml");
+
+       @SuppressWarnings({ "rawtypes" })
+       public Repository getRepository(Map parameters) throws RepositoryException {
+               // // check if can be found by alias
+               // Repository repository = super.getRepository(parameters);
+               // if (repository != null)
+               // return repository;
+
+               // check if remote
+               Repository repository;
+               String uri = null;
+               if (parameters.containsKey(JCR_REPOSITORY_URI))
+                       uri = parameters.get(JCR_REPOSITORY_URI).toString();
+               else if (parameters.containsKey(JcrUtils.REPOSITORY_URI))
+                       uri = parameters.get(JcrUtils.REPOSITORY_URI).toString();
+
+               if (uri != null) {
+                       if (uri.startsWith("http"))// http, https
+                               repository = createRemoteRepository(uri);
+                       else if (uri.startsWith("file"))// http, https
+                               repository = createFileRepository(uri, parameters);
+                       else if (uri.startsWith("vm")) {
+                               log.warn("URI " + uri + " should have been managed by generic JCR repository factory");
+                               repository = getRepositoryByAlias(getAliasFromURI(uri));
+                       } else
+                               throw new ArgeoJcrException("Unrecognized URI format " + uri);
+
+               }
+
+               else if (parameters.containsKey(JCR_REPOSITORY_ALIAS)) {
+                       // Properties properties = new Properties();
+                       // properties.putAll(parameters);
+                       String alias = parameters.get(JCR_REPOSITORY_ALIAS).toString();
+                       // publish(alias, repository, properties);
+                       // log.info("Registered JCR repository under alias '" + alias + "'
+                       // with properties " + properties);
+                       repository = getRepositoryByAlias(alias);
+               } else
+                       throw new ArgeoJcrException("Not enough information in " + parameters);
+
+               if (repository == null)
+                       throw new ArgeoJcrException("Repository not found " + parameters);
+
+               return repository;
+       }
+
+       protected Repository getRepositoryByAlias(String alias) {
+               return null;
+       }
+
+       protected Repository createRemoteRepository(String uri) throws RepositoryException {
+               Map<String, String> params = new HashMap<String, String>();
+               params.put(JcrUtils.REPOSITORY_URI, uri);
+               Repository repository = new Jcr2davRepositoryFactory().getRepository(params);
+               if (repository == null)
+                       throw new ArgeoJcrException("Remote Davex repository " + uri + " not found");
+               log.info("Initialized remote Jackrabbit repository from uri " + uri);
+               return repository;
+       }
+
+       @SuppressWarnings({ "rawtypes", "unchecked" })
+       protected Repository createFileRepository(final String uri, Map parameters) throws RepositoryException {
+               InputStream configurationIn = null;
+               try {
+                       Properties vars = new Properties();
+                       vars.putAll(parameters);
+                       String dirPath = uri.substring("file:".length());
+                       File homeDir = new File(dirPath);
+                       if (homeDir.exists() && !homeDir.isDirectory())
+                               throw new ArgeoJcrException("Repository home " + dirPath + " is not a directory");
+                       if (!homeDir.exists())
+                               homeDir.mkdirs();
+                       configurationIn = fileRepositoryConfiguration.getInputStream();
+                       vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE, homeDir.getCanonicalPath());
+                       RepositoryConfig repositoryConfig = RepositoryConfig.create(new InputSource(configurationIn), vars);
+
+                       // TransientRepository repository = new
+                       // TransientRepository(repositoryConfig);
+                       final RepositoryImpl repository = RepositoryImpl.create(repositoryConfig);
+                       Session session = repository.login();
+                       // FIXME make it generic
+                       org.argeo.jcr.JcrUtils.addPrivilege(session, "/", "ROLE_ADMIN", "jcr:all");
+                       org.argeo.jcr.JcrUtils.logoutQuietly(session);
+                       Runtime.getRuntime().addShutdownHook(new Thread("Clean JCR repository " + uri) {
+                               public void run() {
+                                       repository.shutdown();
+                                       log.info("Destroyed repository " + uri);
+                               }
+                       });
+                       log.info("Initialized file Jackrabbit repository from uri " + uri);
+                       return repository;
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot create repository " + uri, e);
+               } finally {
+                       IOUtils.closeQuietly(configurationIn);
+               }
+       }
+
+       protected String getAliasFromURI(String uri) {
+               try {
+                       URI uriObj = new URI(uri);
+                       String alias = uriObj.getPath();
+                       if (alias.charAt(0) == '/')
+                               alias = alias.substring(1);
+                       if (alias.charAt(alias.length() - 1) == '/')
+                               alias = alias.substring(0, alias.length() - 1);
+                       return alias;
+               } catch (URISyntaxException e) {
+                       throw new ArgeoJcrException("Cannot interpret URI " + uri, e);
+               }
+       }
+
+       /**
+        * Called after the repository has been initialised. Does nothing by
+        * default.
+        */
+       @SuppressWarnings("rawtypes")
+       protected void postInitialization(Repository repository, Map parameters) {
+
+       }
+
+       public void setFileRepositoryConfiguration(Resource fileRepositoryConfiguration) {
+               this.fileRepositoryConfiguration = fileRepositoryConfiguration;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitWrapper.java b/org.argeo.jcr/src/org/argeo/jackrabbit/JackrabbitWrapper.java
new file mode 100644 (file)
index 0000000..268ecdb
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jackrabbit;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Credentials;
+import javax.jcr.LoginException;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.api.JackrabbitRepository;
+import org.apache.jackrabbit.commons.NamespaceHelper;
+import org.apache.jackrabbit.commons.cnd.CndImporter;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.ArgeoJcrException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrRepositoryWrapper;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.util.security.DigestUtils;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+
+/**
+ * Wrapper around a Jackrabbit repository which allows to simplify configuration
+ * and intercept some actions. It exposes itself as a {@link Repository}.
+ */
+@Deprecated
+public class JackrabbitWrapper extends JcrRepositoryWrapper implements
+               JackrabbitRepository, ResourceLoaderAware {
+       private final static Log log = LogFactory.getLog(JackrabbitWrapper.class);
+       private final static String DIGEST_ALGORITHM = "MD5";
+
+       // local
+       private ResourceLoader resourceLoader;
+
+       // data model
+       /** Node type definitions in CND format */
+       private List<String> cndFiles = new ArrayList<String>();
+       /**
+        * Always import CNDs. Useful during development of new data models. In
+        * production, explicit migration processes should be used.
+        */
+       private Boolean forceCndImport = true;
+
+       /** Namespaces to register: key is prefix, value namespace */
+       private Map<String, String> namespaces = new HashMap<String, String>();
+
+       private BundleContext bundleContext;
+
+       /**
+        * Explicitly set admin credentials used in initialization. Useful for
+        * testing, in real applications authentication is rather dealt with
+        * externally
+        */
+       private Credentials adminCredentials = null;
+
+       /**
+        * Empty constructor, {@link #init()} should be called after properties have
+        * been set
+        */
+       public JackrabbitWrapper() {
+       }
+
+       @Override
+       public void init() {
+               prepareDataModel();
+       }
+
+       /*
+        * DATA MODEL
+        */
+
+       /**
+        * Import declared node type definitions and register namespaces. Tries to
+        * update the node definitions if they have changed. In case of failures an
+        * error will be logged but no exception will be thrown.
+        */
+       protected void prepareDataModel() {
+               if ((cndFiles == null || cndFiles.size() == 0)
+                               && (namespaces == null || namespaces.size() == 0))
+                       return;
+
+               Session session = null;
+               try {
+                       session = login(adminCredentials);
+                       // register namespaces
+                       if (namespaces.size() > 0) {
+                               NamespaceHelper namespaceHelper = new NamespaceHelper(session);
+                               namespaceHelper.registerNamespaces(namespaces);
+                       }
+
+                       // load CND files from classpath or as URL
+                       for (String resUrl : cndFiles) {
+                               processCndFile(session, resUrl);
+                       }
+               } catch (Exception e) {
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoJcrException("Cannot import node type definitions "
+                                       + cndFiles, e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+               }
+
+       }
+
+       protected void processCndFile(Session session, String resUrl) {
+               Reader reader = null;
+               try {
+                       // check existing data model nodes
+                       new NamespaceHelper(session).registerNamespace(ArgeoNames.ARGEO,
+                                       ArgeoNames.ARGEO_NAMESPACE);
+                       if (!session.itemExists(ArgeoJcrConstants.DATA_MODELS_BASE_PATH))
+                               JcrUtils.mkdirs(session,
+                                               ArgeoJcrConstants.DATA_MODELS_BASE_PATH);
+                       Node dataModels = session
+                                       .getNode(ArgeoJcrConstants.DATA_MODELS_BASE_PATH);
+                       NodeIterator it = dataModels.getNodes();
+                       Node dataModel = null;
+                       while (it.hasNext()) {
+                               Node node = it.nextNode();
+                               if (node.getProperty(ArgeoNames.ARGEO_URI).getString()
+                                               .equals(resUrl)) {
+                                       dataModel = node;
+                                       break;
+                               }
+                       }
+
+                       byte[] cndContent = readCndContent(resUrl);
+                       String newDigest = DigestUtils.digest(DIGEST_ALGORITHM, cndContent);
+                       Bundle bundle = findDataModelBundle(resUrl);
+
+                       String currentVersion = null;
+                       if (dataModel != null) {
+                               currentVersion = dataModel.getProperty(
+                                               ArgeoNames.ARGEO_DATA_MODEL_VERSION).getString();
+                               if (dataModel.hasNode(Node.JCR_CONTENT)) {
+                                       String oldDigest = JcrUtils.checksumFile(dataModel,
+                                                       DIGEST_ALGORITHM);
+                                       if (oldDigest.equals(newDigest)) {
+                                               if (log.isTraceEnabled())
+                                                       log.trace("Data model " + resUrl
+                                                                       + " hasn't changed, keeping version "
+                                                                       + currentVersion);
+                                               return;
+                                       }
+                               }
+                       }
+
+                       if (dataModel != null && !forceCndImport) {
+                               log.info("Data model "
+                                               + resUrl
+                                               + " has changed since version "
+                                               + currentVersion
+                                               + (bundle != null ? ": version " + bundle.getVersion()
+                                                               + ", bundle " + bundle.getSymbolicName() : ""));
+                               return;
+                       }
+
+                       reader = new InputStreamReader(new ByteArrayInputStream(cndContent));
+                       // actually imports the CND
+                       try {
+                               CndImporter.registerNodeTypes(reader, session, true);
+                       } catch (Exception e) {
+                               log.error("Cannot import data model " + resUrl, e);
+                               return;
+                       }
+
+                       if (dataModel != null && !dataModel.isNodeType(NodeType.NT_FILE)) {
+                               dataModel.remove();
+                               dataModel = null;
+                       }
+
+                       // FIXME: what if argeo.cnd would not be the first called on
+                       // a new repo? argeo:dataModel would not be found
+                       String fileName = FilenameUtils.getName(resUrl);
+                       if (dataModel == null) {
+                               dataModel = dataModels.addNode(fileName, NodeType.NT_FILE);
+                               dataModel.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
+                               dataModel.addMixin(ArgeoTypes.ARGEO_DATA_MODEL);
+                               dataModel.setProperty(ArgeoNames.ARGEO_URI, resUrl);
+                       } else {
+                               session.getWorkspace().getVersionManager()
+                                               .checkout(dataModel.getPath());
+                       }
+                       if (bundle != null)
+                               dataModel.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION,
+                                               bundle.getVersion().toString());
+                       else
+                               dataModel.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION,
+                                               "0.0.0");
+                       JcrUtils.copyBytesAsFile(dataModel.getParent(), fileName,
+                                       cndContent);
+                       JcrUtils.updateLastModified(dataModel);
+                       session.save();
+                       session.getWorkspace().getVersionManager()
+                                       .checkin(dataModel.getPath());
+
+                       if (currentVersion == null)
+                               log.info("Data model "
+                                               + resUrl
+                                               + (bundle != null ? ", version " + bundle.getVersion()
+                                                               + ", bundle " + bundle.getSymbolicName() : ""));
+                       else
+                               log.info("Data model "
+                                               + resUrl
+                                               + " updated from version "
+                                               + currentVersion
+                                               + (bundle != null ? ", version " + bundle.getVersion()
+                                                               + ", bundle " + bundle.getSymbolicName() : ""));
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot process data model " + resUrl, e);
+               } finally {
+                       IOUtils.closeQuietly(reader);
+               }
+       }
+
+       protected byte[] readCndContent(String resUrl) {
+               InputStream in = null;
+               try {
+                       boolean classpath;
+                       // normalize URL
+                       if (bundleContext != null && resUrl.startsWith("classpath:")) {
+                               resUrl = resUrl.substring("classpath:".length());
+                               classpath = true;
+                       } else if (resUrl.indexOf(':') < 0) {
+                               if (!resUrl.startsWith("/")) {
+                                       resUrl = "/" + resUrl;
+                                       log.warn("Classpath should start with '/'");
+                               }
+                               classpath = true;
+                       } else {
+                               classpath = false;
+                       }
+
+                       URL url = null;
+                       if (classpath) {
+                               if (bundleContext != null) {
+                                       Bundle currentBundle = bundleContext.getBundle();
+                                       url = currentBundle.getResource(resUrl);
+                               } else {
+                                       resUrl = "classpath:" + resUrl;
+                                       url = null;
+                               }
+                       } else if (!resUrl.startsWith("classpath:")) {
+                               url = new URL(resUrl);
+                       }
+
+                       if (url != null) {
+                               in = url.openStream();
+                       } else if (resourceLoader != null) {
+                               Resource res = resourceLoader.getResource(resUrl);
+                               in = res.getInputStream();
+                               url = res.getURL();
+                       } else {
+                               throw new ArgeoJcrException("No " + resUrl + " in the classpath,"
+                                               + " make sure the containing" + " package is visible.");
+                       }
+
+                       return IOUtils.toByteArray(in);
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot read CND from " + resUrl, e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+               }
+       }
+
+       /*
+        * JACKRABBIT REPOSITORY IMPLEMENTATION
+        */
+       @Override
+       public Session login(Credentials credentials, String workspaceName,
+                       Map<String, Object> attributes) throws LoginException,
+                       NoSuchWorkspaceException, RepositoryException {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public void shutdown() {
+               // TODO Auto-generated method stub
+
+       }
+
+       /*
+        * UTILITIES
+        */
+       /** Find which OSGi bundle provided the data model resource */
+       protected Bundle findDataModelBundle(String resUrl) {
+               if (bundleContext == null)
+                       return null;
+
+               if (resUrl.startsWith("/"))
+                       resUrl = resUrl.substring(1);
+               String pkg = resUrl.substring(0, resUrl.lastIndexOf('/')).replace('/',
+                               '.');
+               ServiceReference<PackageAdmin> paSr = bundleContext
+                               .getServiceReference(PackageAdmin.class);
+               PackageAdmin packageAdmin = (PackageAdmin) bundleContext
+                               .getService(paSr);
+
+               // find exported package
+               ExportedPackage exportedPackage = null;
+               ExportedPackage[] exportedPackages = packageAdmin
+                               .getExportedPackages(pkg);
+               if (exportedPackages == null)
+                       throw new ArgeoJcrException("No exported package found for " + pkg);
+               for (ExportedPackage ep : exportedPackages) {
+                       for (Bundle b : ep.getImportingBundles()) {
+                               if (b.getBundleId() == bundleContext.getBundle().getBundleId()) {
+                                       exportedPackage = ep;
+                                       break;
+                               }
+                       }
+               }
+
+               Bundle exportingBundle = null;
+               if (exportedPackage != null) {
+                       exportingBundle = exportedPackage.getExportingBundle();
+               } else {
+                       // assume this is in the same bundle
+                       exportingBundle = bundleContext.getBundle();
+                       // throw new ArgeoJcrException("No OSGi exporting package found for "
+                       // + resUrl);
+               }
+               return exportingBundle;
+       }
+
+       /*
+        * FIELDS ACCESS
+        */
+       public void setNamespaces(Map<String, String> namespaces) {
+               this.namespaces = namespaces;
+       }
+
+       public void setCndFiles(List<String> cndFiles) {
+               this.cndFiles = cndFiles;
+       }
+
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+       protected BundleContext getBundleContext() {
+               return bundleContext;
+       }
+
+       public void setForceCndImport(Boolean forceCndUpdate) {
+               this.forceCndImport = forceCndUpdate;
+       }
+
+       public void setResourceLoader(ResourceLoader resourceLoader) {
+               this.resourceLoader = resourceLoader;
+       }
+
+       public void setAdminCredentials(Credentials adminCredentials) {
+               this.adminCredentials = adminCredentials;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/repository-h2.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-h2.xml
new file mode 100644 (file)
index 0000000..0526762
--- /dev/null
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Repository PUBLIC "Jackrabbit 2.6" "http://jackrabbit.apache.org/dtd/repository-2.6.dtd">
+<Repository>
+       <!-- Shared datasource -->
+       <DataSources>
+               <DataSource name="dataSource">
+                       <param name="driver" value="org.h2.Driver" />
+                       <param name="url" value="${dburl}" />
+                       <param name="user" value="${dbuser}" />
+                       <param name="password" value="${dbpassword}" />
+                       <param name="databaseType" value="h2" />
+                       <param name="maxPoolSize" value="${maxPoolSize}" />
+               </DataSource>
+       </DataSources>
+
+       <!-- File system and datastore -->
+       <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+               <param name="dataSourceName" value="dataSource" />
+               <param name="schema" value="default" />
+               <param name="schemaObjectPrefix" value="fs_" />
+       </FileSystem>
+       <DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
+               <param name="path" value="${rep.home}/datastore" />
+       </DataStore>
+
+       <!-- Workspace templates -->
+       <Workspaces rootPath="${rep.home}/workspaces"
+               defaultWorkspace="${defaultWorkspace}" />
+       <Workspace name="${wsp.name}">
+               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schema" value="default" />
+                       <param name="schemaObjectPrefix" value="${wsp.name}_fs_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schemaObjectPrefix" value="${wsp.name}_pm_" />
+                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
+               </PersistenceManager>
+               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+                       <param name="path" value="${wsp.home}/index" />
+                       <param name="extractorPoolSize" value="${extractorPoolSize}" />
+                       <param name="cacheSize" value="${searchCacheSize}" />
+                       <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
+               </SearchIndex>
+               <WorkspaceSecurity>
+                       <AccessControlProvider
+                               class="org.argeo.security.jackrabbit.ArgeoAccessControlProvider" />
+               </WorkspaceSecurity>
+       </Workspace>
+
+       <!-- Versioning -->
+       <Versioning rootPath="${rep.home}/version">
+               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schema" value="default" />
+                       <param name="schemaObjectPrefix" value="fs_ver_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schemaObjectPrefix" value="pm_ver_" />
+                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
+               </PersistenceManager>
+       </Versioning>
+
+       <!-- Indexing -->
+       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+               <param name="path" value="${rep.home}/index" />
+               <param name="extractorPoolSize" value="${extractorPoolSize}" />
+               <param name="cacheSize" value="${searchCacheSize}" />
+               <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security" />
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager" />
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/repository-localfs.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-localfs.xml
new file mode 100644 (file)
index 0000000..3d24708
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Repository PUBLIC "Jackrabbit 2.6" "http://jackrabbit.apache.org/dtd/repository-2.6.dtd">
+<Repository>
+       <!-- File system and datastore -->
+       <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+               <param name="path" value="${rep.home}/repository" />
+       </FileSystem>
+       <DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
+               <param name="path" value="${rep.home}/datastore" />
+       </DataStore>
+
+       <!-- Workspace templates -->
+       <Workspaces rootPath="${rep.home}/workspaces"
+               defaultWorkspace="${defaultWorkspace}" />
+       <Workspace name="${wsp.name}">
+               <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+                       <param name="path" value="${wsp.home}" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
+               </PersistenceManager>
+               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+                       <param name="path" value="${wsp.home}/index" />
+                       <param name="extractorPoolSize" value="${extractorPoolSize}" />
+                       <param name="cacheSize" value="${searchCacheSize}" />
+                       <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
+               </SearchIndex>
+               <WorkspaceSecurity>
+                       <AccessControlProvider
+                               class="org.argeo.security.jackrabbit.ArgeoAccessControlProvider" />
+               </WorkspaceSecurity>
+       </Workspace>
+
+       <!-- Versioning -->
+       <Versioning rootPath="${rep.home}/version">
+               <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+                       <param name="path" value="${rep.home}/version" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
+               </PersistenceManager>
+       </Versioning>
+
+       <!-- Indexing -->
+       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+               <param name="path" value="${rep.home}/index" />
+               <param name="extractorPoolSize" value="${extractorPoolSize}" />
+               <param name="cacheSize" value="${searchCacheSize}" />
+               <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security" />
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager" />
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/repository-memory.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-memory.xml
new file mode 100644 (file)
index 0000000..ecee5bd
--- /dev/null
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<!DOCTYPE Repository PUBLIC "Jackrabbit 2.6" "http://jackrabbit.apache.org/dtd/repository-2.6.dtd">
+<Repository>
+       <!-- File system and datastore -->
+       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+
+       <!-- Workspace templates -->
+       <Workspaces rootPath="${rep.home}/workspaces"
+               defaultWorkspace="${defaultWorkspace}" configRootPath="/workspaces" />
+       <Workspace name="${wsp.name}">
+               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+                       <param name="blobFSBlockSize" value="1" />
+                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
+               </PersistenceManager>
+               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+                       <param name="path" value="${wsp.home}/index" />
+                       <param name="directoryManagerClass"
+                               value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
+                       <param name="extractorPoolSize" value="${extractorPoolSize}" />
+                       <param name="cacheSize" value="${searchCacheSize}" />
+                       <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
+                       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+               </SearchIndex>
+       </Workspace>
+
+       <!-- Versioning -->
+       <Versioning rootPath="${rep.home}/version">
+               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+                       <param name="blobFSBlockSize" value="1" />
+                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
+               </PersistenceManager>
+       </Versioning>
+
+       <!-- Indexing -->
+       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+               <param name="path" value="${rep.home}/index" />
+               <param name="directoryManagerClass"
+                       value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
+               <param name="extractorPoolSize" value="${extractorPoolSize}" />
+               <param name="cacheSize" value="${searchCacheSize}" />
+               <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
+               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security" />
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager" />
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml
new file mode 100644 (file)
index 0000000..07a0d04
--- /dev/null
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<!DOCTYPE Repository PUBLIC "Jackrabbit 2.6" "http://jackrabbit.apache.org/dtd/repository-2.6.dtd">
+<Repository>
+       <!-- Shared datasource -->
+       <DataSources>
+               <DataSource name="dataSource">
+                       <param name="driver" value="org.postgresql.Driver" />
+                       <param name="url" value="${dburl}" />
+                       <param name="user" value="${dbuser}" />
+                       <param name="password" value="${dbpassword}" />
+                       <param name="databaseType" value="postgresql" />
+                       <param name="maxPoolSize" value="${maxPoolSize}" />
+               </DataSource>
+       </DataSources>
+
+       <!-- File system and datastore -->
+       <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+               <param name="dataSourceName" value="dataSource" />
+               <param name="schema" value="postgresql" />
+               <param name="schemaObjectPrefix" value="fs_" />
+       </FileSystem>
+       <DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
+               <param name="path" value="${rep.home}/datastore" />
+       </DataStore>
+
+       <!-- Workspace templates -->
+       <Workspaces rootPath="${rep.home}/workspaces"
+               defaultWorkspace="${defaultWorkspace}" />
+       <Workspace name="${wsp.name}">
+               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schema" value="postgresql" />
+                       <param name="schemaObjectPrefix" value="${wsp.name}_fs_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.PostgreSQLPersistenceManager">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schemaObjectPrefix" value="${wsp.name}_pm_" />
+                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
+               </PersistenceManager>
+               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+                       <param name="path" value="${wsp.home}/index" />
+                       <param name="extractorPoolSize" value="${extractorPoolSize}" />
+                       <param name="cacheSize" value="${searchCacheSize}" />
+                       <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
+               </SearchIndex>
+               <WorkspaceSecurity>
+                       <AccessControlProvider
+                               class="org.argeo.security.jackrabbit.ArgeoAccessControlProvider" />
+               </WorkspaceSecurity>
+       </Workspace>
+
+       <!-- Versioning -->
+       <Versioning rootPath="${rep.home}/version">
+               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schema" value="postgresql" />
+                       <param name="schemaObjectPrefix" value="fs_ver_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.PostgreSQLPersistenceManager">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schemaObjectPrefix" value="pm_ver_" />
+                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
+               </PersistenceManager>
+       </Versioning>
+
+       <!-- Indexing -->
+       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+               <param name="path" value="${rep.home}/index" />
+               <param name="extractorPoolSize" value="${extractorPoolSize}" />
+               <param name="cacheSize" value="${searchCacheSize}" />
+               <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security" />
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager" />
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml
new file mode 100644 (file)
index 0000000..9677828
--- /dev/null
@@ -0,0 +1,79 @@
+<?xml version="1.0"?>
+<!DOCTYPE Repository PUBLIC "Jackrabbit 2.6" "http://jackrabbit.apache.org/dtd/repository-2.6.dtd">
+<Repository>
+       <!-- Shared datasource -->
+       <DataSources>
+               <DataSource name="dataSource">
+                       <param name="driver" value="org.postgresql.Driver" />
+                       <param name="url" value="${dburl}" />
+                       <param name="user" value="${dbuser}" />
+                       <param name="password" value="${dbpassword}" />
+                       <param name="databaseType" value="postgresql" />
+                       <param name="maxPoolSize" value="${maxPoolSize}" />
+               </DataSource>
+       </DataSources>
+
+       <!-- File system and datastore -->
+       <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+               <param name="dataSourceName" value="dataSource" />
+               <param name="schema" value="postgresql" />
+               <param name="schemaObjectPrefix" value="fs_" />
+       </FileSystem>
+
+       <!-- Workspace templates -->
+       <Workspaces rootPath="${rep.home}/workspaces"
+               defaultWorkspace="${defaultWorkspace}" />
+       <Workspace name="${wsp.name}">
+               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schema" value="postgresql" />
+                       <param name="schemaObjectPrefix" value="${wsp.name}_fs_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.PostgreSQLPersistenceManager">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schemaObjectPrefix" value="${wsp.name}_pm_" />
+                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
+               </PersistenceManager>
+               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+                       <param name="path" value="${wsp.home}/index" />
+                       <param name="extractorPoolSize" value="${extractorPoolSize}" />
+                       <param name="cacheSize" value="${searchCacheSize}" />
+                       <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
+               </SearchIndex>
+               <WorkspaceSecurity>
+                       <AccessControlProvider
+                               class="org.argeo.security.jackrabbit.ArgeoAccessControlProvider" />
+               </WorkspaceSecurity>
+       </Workspace>
+
+       <!-- Versioning -->
+       <Versioning rootPath="${rep.home}/version">
+               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schema" value="postgresql" />
+                       <param name="schemaObjectPrefix" value="fs_ver_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.PostgreSQLPersistenceManager">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schemaObjectPrefix" value="pm_ver_" />
+                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
+               </PersistenceManager>
+       </Versioning>
+
+       <!-- Indexing -->
+       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+               <param name="path" value="${rep.home}/index" />
+               <param name="extractorPoolSize" value="${extractorPoolSize}" />
+               <param name="cacheSize" value="${searchCacheSize}" />
+               <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security" />
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager" />
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/OpenInViewSessionProvider.java b/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/OpenInViewSessionProvider.java
new file mode 100644 (file)
index 0000000..52a9883
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jackrabbit.servlet;
+
+import java.io.Serializable;
+
+import javax.jcr.LoginException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.server.SessionProvider;
+import org.argeo.jcr.JcrUtils;
+
+/**
+ * Implements an open session in view patter: a new JCR session is created for
+ * each request
+ */
+@Deprecated
+public class OpenInViewSessionProvider implements SessionProvider, Serializable {
+       private static final long serialVersionUID = 2270957712453841368L;
+
+       private final static Log log = LogFactory
+                       .getLog(OpenInViewSessionProvider.class);
+
+       public Session getSession(HttpServletRequest request, Repository rep,
+                       String workspace) throws LoginException, ServletException,
+                       RepositoryException {
+               return login(request, rep, workspace);
+       }
+
+       protected Session login(HttpServletRequest request, Repository repository,
+                       String workspace) throws RepositoryException {
+               if (log.isTraceEnabled())
+                       log.trace("Login to workspace "
+                                       + (workspace == null ? "<default>" : workspace)
+                                       + " in web session " + request.getSession().getId());
+               return repository.login(workspace);
+       }
+
+       public void releaseSession(Session session) {
+               JcrUtils.logoutQuietly(session);
+               if (log.isTraceEnabled())
+                       log.trace("Logged out remote JCR session " + session);
+       }
+
+       public void init() {
+       }
+
+       public void destroy() {
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/RemotingServlet.java b/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/RemotingServlet.java
new file mode 100644 (file)
index 0000000..3fdb5d2
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jackrabbit.servlet;
+
+import javax.jcr.Repository;
+
+import org.apache.jackrabbit.server.SessionProvider;
+import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet;
+
+/** Provides remote access to a JCR repository */
+@Deprecated
+public class RemotingServlet extends JcrRemotingServlet {
+       public final static String INIT_PARAM_RESOURCE_PATH_PREFIX = JcrRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX;
+       public final static String INIT_PARAM_HOME = JcrRemotingServlet.INIT_PARAM_HOME;
+       public final static String INIT_PARAM_TMP_DIRECTORY = JcrRemotingServlet.INIT_PARAM_TMP_DIRECTORY;
+       public final static String INIT_PARAM_PROTECTED_HANDLERS_CONFIG = JcrRemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG;
+
+       private static final long serialVersionUID = 3131835511468341309L;
+
+       private final Repository repository;
+       private final SessionProvider sessionProvider;
+
+       public RemotingServlet(Repository repository,
+                       SessionProvider sessionProvider) {
+               this.repository = repository;
+               this.sessionProvider = sessionProvider;
+       }
+
+       @Override
+       protected Repository getRepository() {
+               return repository;
+       }
+
+       @Override
+       protected SessionProvider getSessionProvider() {
+               return sessionProvider;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/WebdavServlet.java b/org.argeo.jcr/src/org/argeo/jackrabbit/servlet/WebdavServlet.java
new file mode 100644 (file)
index 0000000..e3176b7
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jackrabbit.servlet;
+
+import java.io.IOException;
+
+import javax.jcr.Repository;
+import javax.servlet.ServletException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.server.SessionProvider;
+import org.apache.jackrabbit.webdav.DavException;
+import org.apache.jackrabbit.webdav.DavResource;
+import org.apache.jackrabbit.webdav.WebdavRequest;
+import org.apache.jackrabbit.webdav.WebdavResponse;
+import org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet;
+
+/** WebDav servlet whose repository is injected */
+@Deprecated
+public class WebdavServlet extends SimpleWebdavServlet {
+       public final static String INIT_PARAM_RESOURCE_CONFIG = SimpleWebdavServlet.INIT_PARAM_RESOURCE_CONFIG;
+       public final static String INIT_PARAM_RESOURCE_PATH_PREFIX = SimpleWebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX;
+
+       private static final long serialVersionUID = -369787931175177080L;
+
+       private final static Log log = LogFactory.getLog(WebdavServlet.class);
+
+       private final Repository repository;
+
+       public WebdavServlet(Repository repository, SessionProvider sessionProvider) {
+               this.repository = repository;
+               setSessionProvider(sessionProvider);
+       }
+
+       public Repository getRepository() {
+               return repository;
+       }
+
+       @Override
+       protected boolean execute(WebdavRequest request, WebdavResponse response,
+                       int method, DavResource resource) throws ServletException,
+                       IOException, DavException {
+               if (log.isTraceEnabled())
+                       log.trace(request.getMethod() + "\t" + request.getPathInfo());
+               boolean res = super.execute(request, response, method, resource);
+               return res;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/unit/AbstractJackrabbitTestCase.java b/org.argeo.jcr/src/org/argeo/jackrabbit/unit/AbstractJackrabbitTestCase.java
new file mode 100644 (file)
index 0000000..1523c83
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jackrabbit.unit;
+
+import java.net.URL;
+
+import javax.jcr.Repository;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.argeo.jcr.unit.AbstractJcrTestCase;
+
+/** Factorizes configuration of an in memory transient repository */
+public abstract class AbstractJackrabbitTestCase extends AbstractJcrTestCase {
+       protected RepositoryImpl repositoryImpl;
+
+       // protected File getRepositoryFile() throws Exception {
+       // Resource res = new ClassPathResource(
+       // "org/argeo/jackrabbit/unit/repository-memory.xml");
+       // return res.getFile();
+       // }
+
+       public AbstractJackrabbitTestCase() {
+               URL url = AbstractJackrabbitTestCase.class.getResource("jaas.config");
+               assert url != null;
+               System.setProperty("java.security.auth.login.config", url.toString());
+       }
+
+       protected Repository createRepository() throws Exception {
+               // Repository repository = new TransientRepository(getRepositoryFile(),
+               // getHomeDir());
+               RepositoryConfig repositoryConfig = RepositoryConfig.create(
+                               AbstractJackrabbitTestCase.class
+                                               .getResourceAsStream(getRepositoryConfigResource()),
+                               getHomeDir().getAbsolutePath());
+               RepositoryImpl repositoryImpl = RepositoryImpl.create(repositoryConfig);
+               return repositoryImpl;
+       }
+
+       protected String getRepositoryConfigResource() {
+               return "repository-memory.xml";
+       }
+
+       @Override
+       protected void clearRepository(Repository repository) throws Exception {
+               RepositoryImpl repositoryImpl = (RepositoryImpl) repository;
+               if (repositoryImpl != null)
+                       repositoryImpl.shutdown();
+               FileUtils.deleteDirectory(getHomeDir());
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/unit/jaas.config b/org.argeo.jcr/src/org/argeo/jackrabbit/unit/jaas.config
new file mode 100644 (file)
index 0000000..11d2f96
--- /dev/null
@@ -0,0 +1,7 @@
+TEST_JACKRABBIT_ADMIN {
+   org.argeo.jackrabbit.JackrabbitAdminLoginModule requisite;
+};
+
+Jackrabbit {
+   org.argeo.security.jackrabbit.SystemJackrabbitLoginModule requisite;
+};
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/unit/repository-h2.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/unit/repository-h2.xml
new file mode 100644 (file)
index 0000000..348dc28
--- /dev/null
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
+                            "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
+<Repository>
+       <!-- Shared datasource -->
+       <DataSources>
+               <DataSource name="dataSource">
+                       <param name="driver" value="org.h2.Driver" />
+                       <param name="url" value="jdbc:h2:mem:jackrabbit" />
+                       <param name="user" value="sa" />
+                       <param name="password" value="" />
+                       <param name="databaseType" value="h2" />
+                       <param name="maxPoolSize" value="10" />
+               </DataSource>
+       </DataSources>
+
+       <!-- File system and datastore -->
+       <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+               <param name="dataSourceName" value="dataSource" />
+               <param name="schema" value="default" />
+               <param name="schemaObjectPrefix" value="fs_" />
+       </FileSystem>
+       <DataStore class="org.apache.jackrabbit.core.data.db.DbDataStore">
+               <param name="dataSourceName" value="dataSource" />
+               <param name="schemaObjectPrefix" value="ds_" />
+       </DataStore>
+
+       <!-- Workspace templates -->
+       <Workspaces rootPath="${rep.home}/workspaces"
+               defaultWorkspace="dev" />
+       <Workspace name="${wsp.name}">
+               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schema" value="default" />
+                       <param name="schemaObjectPrefix" value="${wsp.name}_fs_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schemaObjectPrefix" value="${wsp.name}_pm_" />
+               </PersistenceManager>
+               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+                       <param name="path" value="${wsp.home}/index" />
+               </SearchIndex>
+       </Workspace>
+
+       <!-- Versioning -->
+       <Versioning rootPath="${rep.home}/version">
+               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schema" value="default" />
+                       <param name="schemaObjectPrefix" value="fs_ver_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schemaObjectPrefix" value="pm_ver_" />
+               </PersistenceManager>
+       </Versioning>
+
+       <!-- Indexing -->
+       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+               <param name="path" value="${rep.home}/repository/index" />
+               <param name="extractorPoolSize" value="2" />
+               <param name="supportHighlighting" value="true" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager
+                       class="org.apache.jackrabbit.core.security.simple.SimpleSecurityManager"
+                       workspaceName="security" />
+               <AccessManager
+                       class="org.apache.jackrabbit.core.security.simple.SimpleAccessManager" />
+               <LoginModule
+                       class="org.apache.jackrabbit.core.security.simple.SimpleLoginModule">
+                       <param name="anonymousId" value="anonymous" />
+                       <param name="adminId" value="admin" />
+               </LoginModule>
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/org.argeo.jcr/src/org/argeo/jackrabbit/unit/repository-memory.xml b/org.argeo.jcr/src/org/argeo/jackrabbit/unit/repository-memory.xml
new file mode 100644 (file)
index 0000000..8395424
--- /dev/null
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (C) 2007-2012 Argeo GmbH
+
+    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.
+
+-->
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
+                            "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
+<Repository>
+       <!-- File system and datastore -->
+       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+
+       <!-- Workspace templates -->
+       <Workspaces rootPath="${rep.home}/workspaces"
+               defaultWorkspace="main" configRootPath="/workspaces" />
+       <Workspace name="${wsp.name}">
+               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+                       <param name="blobFSBlockSize" value="1" />
+               </PersistenceManager>
+               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+                       <param name="path" value="${rep.home}/repository/index" />
+                       <param name="directoryManagerClass"
+                               value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
+                       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+               </SearchIndex>
+       </Workspace>
+
+       <!-- Versioning -->
+       <Versioning rootPath="${rep.home}/version">
+               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+                       <param name="blobFSBlockSize" value="1" />
+               </PersistenceManager>
+       </Versioning>
+
+       <!-- Indexing -->
+       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+               <param name="path" value="${rep.home}/repository/index" />
+               <param name="directoryManagerClass"
+                       value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
+               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager
+                       class="org.apache.jackrabbit.core.security.simple.SimpleSecurityManager"
+                       workspaceName="security" />
+               <AccessManager
+                       class="org.apache.jackrabbit.core.security.simple.SimpleAccessManager" />
+               <LoginModule
+                       class="org.apache.jackrabbit.core.security.simple.SimpleLoginModule">
+                       <param name="anonymousId" value="anonymous" />
+                       <param name="adminId" value="admin" />
+               </LoginModule>
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java b/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java
new file mode 100644 (file)
index 0000000..26979e9
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import javax.jcr.Repository;
+
+/** Argeo model specific constants */
+public interface ArgeoJcrConstants {
+       public final static String ARGEO_BASE_PATH = "/argeo:system";
+       public final static String DATA_MODELS_BASE_PATH = ARGEO_BASE_PATH + "/argeo:dataModels";
+       public final static String PEOPLE_BASE_PATH = ARGEO_BASE_PATH + "/argeo:people";
+
+       // parameters (typically for call to a RepositoryFactory)
+       /** Key for a JCR repository alias */
+       public final static String JCR_REPOSITORY_ALIAS = "argeo.jcr.repository.alias";
+       /** Key for a JCR repository URI */
+       public final static String JCR_REPOSITORY_URI = "argeo.jcr.repository.uri";
+
+       // standard aliases
+       /**
+        * Reserved alias for the "node" {@link Repository}, that is, the default
+        * JCR repository.
+        */
+       public final static String ALIAS_NODE = "node";
+       public final static String ALIAS_HOME = "home";
+       public final static String BASE_REPO_PID = "argeo.repo.";
+       public final static String REPO_PID_NODE = BASE_REPO_PID + ALIAS_NODE;
+       public final static String JACKRABBIT_REPO_FACTORY_PID = "argeo.repo.factory.jackrabbit";
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrException.java b/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrException.java
new file mode 100644 (file)
index 0000000..8e19593
--- /dev/null
@@ -0,0 +1,15 @@
+package org.argeo.jcr;
+
+/** Argeo JCR specific exceptions. */
+public class ArgeoJcrException extends RuntimeException {
+       private static final long serialVersionUID = -1941940005390084331L;
+
+       public ArgeoJcrException(String message, Throwable cause) {
+               super(message, cause);
+       }
+
+       public ArgeoJcrException(String message) {
+               super(message);
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrUtils.java b/org.argeo.jcr/src/org/argeo/jcr/ArgeoJcrUtils.java
new file mode 100644 (file)
index 0000000..9a0fd19
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+
+/** Utilities related to Argeo model in JCR */
+public class ArgeoJcrUtils implements ArgeoJcrConstants {
+       /**
+        * Wraps the call to the repository factory based on parameter
+        * {@link ArgeoJcrConstants#JCR_REPOSITORY_ALIAS} in order to simplify it
+        * and protect against future API changes.
+        */
+       public static Repository getRepositoryByAlias(
+                       RepositoryFactory repositoryFactory, String alias) {
+               try {
+                       Map<String, String> parameters = new HashMap<String, String>();
+                       parameters.put(JCR_REPOSITORY_ALIAS, alias);
+                       return repositoryFactory.getRepository(parameters);
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException(
+                                       "Unexpected exception when trying to retrieve repository with alias "
+                                                       + alias, e);
+               }
+       }
+
+       /**
+        * Wraps the call to the repository factory based on parameter
+        * {@link ArgeoJcrConstants#JCR_REPOSITORY_URI} in order to simplify it and
+        * protect against future API changes.
+        */
+       public static Repository getRepositoryByUri(
+                       RepositoryFactory repositoryFactory, String uri) {
+               return getRepositoryByUri(repositoryFactory, uri, null);
+       }
+
+       /**
+        * Wraps the call to the repository factory based on parameter
+        * {@link ArgeoJcrConstants#JCR_REPOSITORY_URI} in order to simplify it and
+        * protect against future API changes.
+        */
+       public static Repository getRepositoryByUri(
+                       RepositoryFactory repositoryFactory, String uri, String alias) {
+               try {
+                       Map<String, String> parameters = new HashMap<String, String>();
+                       parameters.put(JCR_REPOSITORY_URI, uri);
+                       if (alias != null)
+                               parameters.put(JCR_REPOSITORY_ALIAS, alias);
+                       return repositoryFactory.getRepository(parameters);
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException(
+                                       "Unexpected exception when trying to retrieve repository with uri "
+                                                       + uri, e);
+               }
+       }
+
+       private ArgeoJcrUtils() {
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/ArgeoNames.java b/org.argeo.jcr/src/org/argeo/jcr/ArgeoNames.java
new file mode 100644 (file)
index 0000000..1d4582a
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+/** JCR names in the http://www.argeo.org/argeo namespace */
+public interface ArgeoNames {
+       public final static String ARGEO_NAMESPACE = "http://www.argeo.org/ns/argeo";
+       public final static String ARGEO = "argeo";
+
+       public final static String ARGEO_URI = "argeo:uri";
+       public final static String ARGEO_USER_ID = "argeo:userID";
+       public final static String ARGEO_PREFERENCES = "argeo:preferences";
+       public final static String ARGEO_DATA_MODEL_VERSION = "argeo:dataModelVersion";
+
+       public final static String ARGEO_REMOTE = "argeo:remote";
+       public final static String ARGEO_PASSWORD = "argeo:password";
+//     public final static String ARGEO_REMOTE_ROLES = "argeo:remoteRoles";
+
+       // user profile
+       public final static String ARGEO_PROFILE = "argeo:profile";
+
+       // spring security
+       public final static String ARGEO_ENABLED = "argeo:enabled";
+       public final static String ARGEO_ACCOUNT_NON_EXPIRED = "argeo:accountNonExpired";
+       public final static String ARGEO_ACCOUNT_NON_LOCKED = "argeo:accountNonLocked";
+       public final static String ARGEO_CREDENTIALS_NON_EXPIRED = "argeo:credentialsNonExpired";
+
+       // personal details
+       public final static String ARGEO_FIRST_NAME = "argeo:firstName";
+       public final static String ARGEO_LAST_NAME = "argeo:lastName";
+       public final static String ARGEO_PRIMARY_EMAIL = "argeo:primaryEmail";
+       public final static String ARGEO_PRIMARY_ORGANIZATION = "argeo:primaryOrganization";
+
+       // tabular
+       public final static String ARGEO_IS_KEY = "argeo:isKey";
+
+       // crypto
+       public final static String ARGEO_IV = "argeo:iv";
+       public final static String ARGEO_SECRET_KEY_FACTORY = "argeo:secretKeyFactory";
+       public final static String ARGEO_SALT = "argeo:salt";
+       public final static String ARGEO_ITERATION_COUNT = "argeo:iterationCount";
+       public final static String ARGEO_KEY_LENGTH = "argeo:keyLength";
+       public final static String ARGEO_SECRET_KEY_ENCRYPTION = "argeo:secretKeyEncryption";
+       public final static String ARGEO_CIPHER = "argeo:cipher";
+       public final static String ARGEO_KEYRING = "argeo:keyring";
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/ArgeoTypes.java b/org.argeo.jcr/src/org/argeo/jcr/ArgeoTypes.java
new file mode 100644 (file)
index 0000000..a11ead5
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+/** JCR types in the http://www.argeo.org/argeo namespace */
+public interface ArgeoTypes {
+       public final static String ARGEO_LINK = "argeo:link";
+       public final static String ARGEO_USER_HOME = "argeo:userHome";
+       public final static String ARGEO_USER_PROFILE = "argeo:userProfile";
+       public final static String ARGEO_REMOTE_REPOSITORY = "argeo:remoteRepository";
+       public final static String ARGEO_PREFERENCE_NODE = "argeo:preferenceNode";
+
+       // data model
+       public final static String ARGEO_DATA_MODEL = "argeo:dataModel";
+       
+       // tabular
+       public final static String ARGEO_TABLE = "argeo:table";
+       public final static String ARGEO_COLUMN = "argeo:column";
+       public final static String ARGEO_CSV = "argeo:csv";
+
+       // crypto
+       public final static String ARGEO_ENCRYPTED = "argeo:encrypted";
+       public final static String ARGEO_PBE_SPEC = "argeo:pbeSpec";
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/CollectionNodeIterator.java b/org.argeo.jcr/src/org/argeo/jcr/CollectionNodeIterator.java
new file mode 100644 (file)
index 0000000..a65907a
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+
+/** Wraps a collection of nodes in order to read it as a {@link NodeIterator} */
+public class CollectionNodeIterator implements NodeIterator {
+       private final Long collectionSize;
+       private final Iterator<Node> iterator;
+       private Integer position = 0;
+
+       public CollectionNodeIterator(Collection<Node> nodes) {
+               super();
+               this.collectionSize = (long) nodes.size();
+               this.iterator = nodes.iterator();
+       }
+
+       public void skip(long skipNum) {
+               if (skipNum < 0)
+                       throw new IllegalArgumentException(
+                                       "Skip count has to be positive: " + skipNum);
+
+               for (long i = 0; i < skipNum; i++) {
+                       if (!hasNext())
+                               throw new NoSuchElementException("Last element past (position="
+                                               + getPosition() + ")");
+                       nextNode();
+               }
+       }
+
+       public long getSize() {
+               return collectionSize;
+       }
+
+       public long getPosition() {
+               return position;
+       }
+
+       public boolean hasNext() {
+               return iterator.hasNext();
+       }
+
+       public Object next() {
+               return nextNode();
+       }
+
+       public void remove() {
+               iterator.remove();
+       }
+
+       public Node nextNode() {
+               Node node = iterator.next();
+               position++;
+               return node;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/DefaultJcrListener.java b/org.argeo.jcr/src/org/argeo/jcr/DefaultJcrListener.java
new file mode 100644 (file)
index 0000000..5ef8edd
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+import javax.jcr.observation.ObservationManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/** To be overridden */
+public class DefaultJcrListener implements EventListener {
+       private final static Log log = LogFactory.getLog(DefaultJcrListener.class);
+       private Session session;
+       private String path = "/";
+       private Boolean deep = true;
+
+       public void start() {
+               try {
+                       addEventListener(session().getWorkspace().getObservationManager());
+                       if (log.isDebugEnabled())
+                               log.debug("Registered JCR event listener on " + path);
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot register event listener", e);
+               }
+       }
+
+       public void stop() {
+               try {
+                       session().getWorkspace().getObservationManager()
+                                       .removeEventListener(this);
+                       if (log.isDebugEnabled())
+                               log.debug("Unregistered JCR event listener on " + path);
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot unregister event listener", e);
+               }
+       }
+
+       /** Default is listen to all events */
+       protected Integer getEvents() {
+               return Event.NODE_ADDED | Event.NODE_REMOVED | Event.PROPERTY_ADDED
+                               | Event.PROPERTY_CHANGED | Event.PROPERTY_REMOVED;
+       }
+
+       /** To be overidden */
+       public void onEvent(EventIterator events) {
+               while (events.hasNext()) {
+                       Event event = events.nextEvent();
+                       log.debug(event);
+               }
+       }
+
+       /** To be overidden */
+       protected void addEventListener(ObservationManager observationManager)
+                       throws RepositoryException {
+               observationManager.addEventListener(this, getEvents(), path, deep,
+                               null, null, false);
+       }
+
+       private Session session() {
+               return session;
+       }
+
+       public void setPath(String path) {
+               this.path = path;
+       }
+
+       public void setDeep(Boolean deep) {
+               this.deep = deep;
+       }
+
+       public void setSession(Session session) {
+               this.session = session;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/DefaultRepositoryFactory.java b/org.argeo.jcr/src/org/argeo/jcr/DefaultRepositoryFactory.java
new file mode 100644 (file)
index 0000000..ccfc269
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+
+/**
+ * Simple implementation of {@link RepositoryFactory}, supporting OSGi aliases.
+ */
+@Deprecated
+public class DefaultRepositoryFactory extends DefaultRepositoryRegister
+               implements RepositoryFactory, ArgeoJcrConstants {
+       @SuppressWarnings("rawtypes")
+       public Repository getRepository(Map parameters) throws RepositoryException {
+               if (parameters.containsKey(JCR_REPOSITORY_ALIAS)) {
+                       String alias = parameters.get(JCR_REPOSITORY_ALIAS).toString();
+                       return getRepositoryByAlias(alias);
+               } else if (parameters.containsKey(JCR_REPOSITORY_URI)) {
+                       String uri = parameters.get(JCR_REPOSITORY_URI).toString();
+                       return getRepositoryByAlias(getAliasFromURI(uri));
+               }
+               return null;
+       }
+
+       protected String getAliasFromURI(String uri) {
+               try {
+                       URI uriObj = new URI(uri);
+                       String alias = uriObj.getPath();
+                       if (alias.charAt(0) == '/')
+                               alias = alias.substring(1);
+                       if (alias.charAt(alias.length() - 1) == '/')
+                               alias = alias.substring(0, alias.length() - 1);
+                       return alias;
+               } catch (URISyntaxException e) {
+                       throw new ArgeoJcrException("Cannot interpret URI " + uri, e);
+               }
+       }
+
+       /**
+        * Retrieve a repository by alias
+        * 
+        * @return the repository registered with alias or null if none
+        */
+       protected Repository getRepositoryByAlias(String alias) {
+               if (getRepositories().containsKey(alias))
+                       return getRepositories().get(alias);
+               else
+                       return null;
+       }
+
+       protected void publish(String alias, Repository repository,
+                       Properties properties) {
+               register(repository, properties);
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/DefaultRepositoryRegister.java b/org.argeo.jcr/src/org/argeo/jcr/DefaultRepositoryRegister.java
new file mode 100644 (file)
index 0000000..e2a5026
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Observable;
+import java.util.TreeMap;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+@Deprecated
+public class DefaultRepositoryRegister extends Observable implements
+               RepositoryRegister, ArgeoJcrConstants {
+       private final static Log log = LogFactory
+                       .getLog(DefaultRepositoryRegister.class);
+
+       /** Read only map which will be directly exposed. */
+       private Map<String, Repository> repositories = Collections
+                       .unmodifiableMap(new TreeMap<String, Repository>());
+
+       @SuppressWarnings("rawtypes")
+       public synchronized Repository getRepository(Map parameters)
+                       throws RepositoryException {
+               if (!parameters.containsKey(JCR_REPOSITORY_ALIAS))
+                       throw new RepositoryException("Parameter " + JCR_REPOSITORY_ALIAS
+                                       + " has to be defined.");
+               String alias = parameters.get(JCR_REPOSITORY_ALIAS).toString();
+               if (!repositories.containsKey(alias))
+                       throw new RepositoryException(
+                                       "No repository registered with alias " + alias);
+
+               return repositories.get(alias);
+       }
+
+       /** Access to the read-only map */
+       public synchronized Map<String, Repository> getRepositories() {
+               return repositories;
+       }
+
+       /** Registers a service, typically called when OSGi services are bound. */
+       @SuppressWarnings("rawtypes")
+       public synchronized void register(Repository repository, Map properties) {
+               String alias;
+               if (properties == null || !properties.containsKey(JCR_REPOSITORY_ALIAS)) {
+                       log.warn("Cannot register a repository if no "
+                                       + JCR_REPOSITORY_ALIAS + " property is specified.");
+                       return;
+               }
+               alias = properties.get(JCR_REPOSITORY_ALIAS).toString();
+               Map<String, Repository> map = new TreeMap<String, Repository>(
+                               repositories);
+               map.put(alias, repository);
+               repositories = Collections.unmodifiableMap(map);
+               setChanged();
+               notifyObservers(alias);
+       }
+
+       /** Unregisters a service, typically called when OSGi services are unbound. */
+       @SuppressWarnings("rawtypes")
+       public synchronized void unregister(Repository repository, Map properties) {
+               // TODO: also check bean name?
+               if (properties == null || !properties.containsKey(JCR_REPOSITORY_ALIAS)) {
+                       log.warn("Cannot unregister a repository without property "
+                                       + JCR_REPOSITORY_ALIAS);
+                       return;
+               }
+
+               String alias = properties.get(JCR_REPOSITORY_ALIAS).toString();
+               Map<String, Repository> map = new TreeMap<String, Repository>(
+                               repositories);
+               if (map.remove(alias) == null) {
+                       log.warn("No repository was registered with alias " + alias);
+                       return;
+               }
+               repositories = Collections.unmodifiableMap(map);
+               setChanged();
+               notifyObservers(alias);
+       }
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/JcrCallback.java b/org.argeo.jcr/src/org/argeo/jcr/JcrCallback.java
new file mode 100644 (file)
index 0000000..0c4706f
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import javax.jcr.Session;
+
+/** An arbitrary execution on a JCR session, optionally returning a result. */
+public interface JcrCallback {
+       public Object execute(Session session);
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/JcrMonitor.java b/org.argeo.jcr/src/org/argeo/jcr/JcrMonitor.java
new file mode 100644 (file)
index 0000000..f04be9a
--- /dev/null
@@ -0,0 +1,89 @@
+package org.argeo.jcr;
+
+import org.argeo.ArgeoMonitor;
+
+/**
+ * Simple monitor abstraction. Inspired by Eclipse IProgressMOnitor, but without
+ * dependency to it.
+ */
+@SuppressWarnings("deprecation")
+public interface JcrMonitor extends ArgeoMonitor {
+       /**
+        * Constant indicating an unknown amount of work.
+        */
+       public final static int UNKNOWN = -1;
+
+       /**
+        * Notifies that the main task is beginning. This must only be called once
+        * on a given progress monitor instance.
+        * 
+        * @param name
+        *            the name (or description) of the main task
+        * @param totalWork
+        *            the total number of work units into which the main task is
+        *            been subdivided. If the value is <code>UNKNOWN</code> the
+        *            implementation is free to indicate progress in a way which
+        *            doesn't require the total number of work units in advance.
+        */
+       public void beginTask(String name, int totalWork);
+
+       /**
+        * Notifies that the work is done; that is, either the main task is
+        * completed or the user canceled it. This method may be called more than
+        * once (implementations should be prepared to handle this case).
+        */
+       public void done();
+
+       /**
+        * Returns whether cancelation of current operation has been requested.
+        * Long-running operations should poll to see if cancelation has been
+        * requested.
+        * 
+        * @return <code>true</code> if cancellation has been requested, and
+        *         <code>false</code> otherwise
+        * @see #setCanceled(boolean)
+        */
+       public boolean isCanceled();
+
+       /**
+        * Sets the cancel state to the given value.
+        * 
+        * @param value
+        *            <code>true</code> indicates that cancelation has been
+        *            requested (but not necessarily acknowledged);
+        *            <code>false</code> clears this flag
+        * @see #isCanceled()
+        */
+       public void setCanceled(boolean value);
+
+       /**
+        * Sets the task name to the given value. This method is used to restore the
+        * task label after a nested operation was executed. Normally there is no
+        * need for clients to call this method.
+        * 
+        * @param name
+        *            the name (or description) of the main task
+        * @see #beginTask(java.lang.String, int)
+        */
+       public void setTaskName(String name);
+
+       /**
+        * Notifies that a subtask of the main task is beginning. Subtasks are
+        * optional; the main task might not have subtasks.
+        * 
+        * @param name
+        *            the name (or description) of the subtask
+        */
+       public void subTask(String name);
+
+       /**
+        * Notifies that a given number of work unit of the main task has been
+        * completed. Note that this amount represents an installment, as opposed to
+        * a cumulative amount of work done to date.
+        * 
+        * @param work
+        *            a non-negative number of work units just completed
+        */
+       public void worked(int work);
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/JcrRepositoryWrapper.java b/org.argeo.jcr/src/org/argeo/jcr/JcrRepositoryWrapper.java
new file mode 100644 (file)
index 0000000..6c23aca
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import javax.jcr.Credentials;
+import javax.jcr.LoginException;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+
+/**
+ * Wrapper around a JCR repository which allows to simplify configuration and
+ * intercept some actions. It exposes itself as a {@link Repository}.
+ */
+public abstract class JcrRepositoryWrapper implements Repository {
+       // private final static Log log = LogFactory
+       // .getLog(JcrRepositoryWrapper.class);
+
+       // wrapped repository
+       private Repository repository;
+
+       private Boolean autocreateWorkspaces = false;
+
+       /**
+        * Empty constructor, {@link #init()} should be called after properties have
+        * been set
+        */
+       public JcrRepositoryWrapper() {
+       }
+
+       /** Initializes */
+       public void init() {
+       }
+
+       /** Shutdown the repository */
+       public void destroy() throws Exception {
+       }
+
+       /*
+        * DELEGATED JCR REPOSITORY METHODS
+        */
+
+       public String getDescriptor(String key) {
+               return getRepository().getDescriptor(key);
+       }
+
+       public String[] getDescriptorKeys() {
+               return getRepository().getDescriptorKeys();
+       }
+
+       /** Central login method */
+       public Session login(Credentials credentials, String workspaceName)
+                       throws LoginException, NoSuchWorkspaceException,
+                       RepositoryException {
+               Session session;
+               try {
+                       session = getRepository().login(credentials, workspaceName);
+               } catch (NoSuchWorkspaceException e) {
+                       if (autocreateWorkspaces && workspaceName != null)
+                               session = createWorkspaceAndLogsIn(credentials, workspaceName);
+                       else
+                               throw e;
+               }
+               processNewSession(session);
+               return session;
+       }
+
+       public Session login() throws LoginException, RepositoryException {
+               return login(null, null);
+       }
+
+       public Session login(Credentials credentials) throws LoginException,
+                       RepositoryException {
+               return login(credentials, null);
+       }
+
+       public Session login(String workspaceName) throws LoginException,
+                       NoSuchWorkspaceException, RepositoryException {
+               return login(null, workspaceName);
+       }
+
+       /** Called after a session has been created, does nothing by default. */
+       protected void processNewSession(Session session) {
+       }
+
+       /** Wraps access to the repository, making sure it is available. */
+       protected synchronized Repository getRepository() {
+//             if (repository == null) {
+//                     throw new ArgeoJcrException("No repository initialized."
+//                                     + " Was the init() method called?"
+//                                     + " The destroy() method should also"
+//                                     + " be called on shutdown.");
+//             }
+               return repository;
+       }
+
+       /**
+        * Logs in to the default workspace, creates the required workspace, logs
+        * out, logs in to the required workspace.
+        */
+       protected Session createWorkspaceAndLogsIn(Credentials credentials,
+                       String workspaceName) throws RepositoryException {
+               if (workspaceName == null)
+                       throw new ArgeoJcrException("No workspace specified.");
+               Session session = getRepository().login(credentials);
+               session.getWorkspace().createWorkspace(workspaceName);
+               session.logout();
+               return getRepository().login(credentials, workspaceName);
+       }
+
+       public boolean isStandardDescriptor(String key) {
+               return getRepository().isStandardDescriptor(key);
+       }
+
+       public boolean isSingleValueDescriptor(String key) {
+               return getRepository().isSingleValueDescriptor(key);
+       }
+
+       public Value getDescriptorValue(String key) {
+               return getRepository().getDescriptorValue(key);
+       }
+
+       public Value[] getDescriptorValues(String key) {
+               return getRepository().getDescriptorValues(key);
+       }
+
+       public synchronized void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setAutocreateWorkspaces(Boolean autocreateWorkspaces) {
+               this.autocreateWorkspaces = autocreateWorkspaces;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/JcrResourceAdapter.java b/org.argeo.jcr/src/org/argeo/jcr/JcrResourceAdapter.java
new file mode 100644 (file)
index 0000000..1ccce4f
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionHistory;
+import javax.jcr.version.VersionIterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Bridge Spring resources and JCR folder / files semantics (nt:folder /
+ * nt:file), supporting versioning as well.
+ */
+public class JcrResourceAdapter {
+       private final static Log log = LogFactory.getLog(JcrResourceAdapter.class);
+
+       private Session session;
+
+       private Boolean versioning = true;
+       private String defaultEncoding = "UTF-8";
+
+       // private String restoreBase = "/.restore";
+
+       public JcrResourceAdapter() {
+       }
+
+       public JcrResourceAdapter(Session session) {
+               this.session = session;
+       }
+
+       public void mkdirs(String path) {
+               JcrUtils.mkdirs(session(), path, NodeType.NT_FOLDER,
+                               NodeType.NT_FOLDER, versioning);
+       }
+
+       public void create(String path, InputStream in, String mimeType) {
+               try {
+                       if (session().itemExists(path)) {
+                               throw new ArgeoJcrException("Node " + path + " already exists.");
+                       }
+
+                       int index = path.lastIndexOf('/');
+                       String parentPath = path.substring(0, index);
+                       if (parentPath.equals(""))
+                               parentPath = "/";
+                       String fileName = path.substring(index + 1);
+                       if (!session().itemExists(parentPath))
+                               throw new ArgeoJcrException("Parent folder of node " + path
+                                               + " does not exist: " + parentPath);
+
+                       Node folderNode = (Node) session().getItem(parentPath);
+                       Node fileNode = folderNode.addNode(fileName, "nt:file");
+
+                       Node contentNode = fileNode.addNode(Property.JCR_CONTENT,
+                                       "nt:resource");
+                       if (mimeType != null)
+                               contentNode.setProperty(Property.JCR_MIMETYPE, mimeType);
+                       contentNode.setProperty(Property.JCR_ENCODING, defaultEncoding);
+                       Binary binary = session().getValueFactory().createBinary(in);
+                       contentNode.setProperty(Property.JCR_DATA, binary);
+                       JcrUtils.closeQuietly(binary);
+                       Calendar lastModified = Calendar.getInstance();
+                       // lastModified.setTimeInMillis(file.lastModified());
+                       contentNode.setProperty(Property.JCR_LAST_MODIFIED, lastModified);
+                       // resNode.addMixin("mix:referenceable");
+
+                       if (versioning)
+                               fileNode.addMixin("mix:versionable");
+
+                       session().save();
+
+                       if (versioning)
+                               session().getWorkspace().getVersionManager()
+                                               .checkin(fileNode.getPath());
+
+                       if (log.isDebugEnabled())
+                               log.debug("Created " + path);
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot create node for " + path, e);
+               }
+
+       }
+
+       public void update(String path, InputStream in) {
+               try {
+
+                       if (!session().itemExists(path)) {
+                               String type = null;
+                               // FIXME: using javax.activation leads to conflict between Java
+                               // 1.5 and 1.6 (since javax.activation was included in Java 1.6)
+                               // String type = new MimetypesFileTypeMap()
+                               // .getContentType(FilenameUtils.getName(path));
+                               create(path, in, type);
+                               return;
+                       }
+
+                       Node fileNode = (Node) session().getItem(path);
+                       Node contentNode = fileNode.getNode(Property.JCR_CONTENT);
+                       if (versioning)
+                               session().getWorkspace().getVersionManager()
+                                               .checkout(fileNode.getPath());
+                       Binary binary = session().getValueFactory().createBinary(in);
+                       contentNode.setProperty(Property.JCR_DATA, binary);
+                       JcrUtils.closeQuietly(binary);
+                       Calendar lastModified = Calendar.getInstance();
+                       // lastModified.setTimeInMillis(file.lastModified());
+                       contentNode.setProperty(Property.JCR_LAST_MODIFIED, lastModified);
+
+                       session().save();
+                       if (versioning)
+                               session().getWorkspace().getVersionManager()
+                                               .checkin(fileNode.getPath());
+
+                       if (log.isDebugEnabled())
+                               log.debug("Updated " + path);
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot update node " + path, e);
+               }
+       }
+
+       public List<Calendar> listVersions(String path) {
+               if (!versioning)
+                       throw new ArgeoJcrException("Versioning is not activated");
+
+               try {
+                       List<Calendar> versions = new ArrayList<Calendar>();
+                       Node fileNode = (Node) session().getItem(path);
+                       VersionHistory history = session().getWorkspace()
+                                       .getVersionManager().getVersionHistory(fileNode.getPath());
+                       for (VersionIterator it = history.getAllVersions(); it.hasNext();) {
+                               Version version = (Version) it.next();
+                               versions.add(version.getCreated());
+                               if (log.isTraceEnabled()) {
+                                       log.debug(version);
+                                       // debug(version);
+                               }
+                       }
+                       return versions;
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot list version of node " + path, e);
+               }
+       }
+
+       public InputStream retrieve(String path) {
+               try {
+                       Node node = (Node) session().getItem(
+                                       path + "/" + Property.JCR_CONTENT);
+                       Property property = node.getProperty(Property.JCR_DATA);
+                       return property.getBinary().getStream();
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot retrieve " + path, e);
+               }
+       }
+
+       public synchronized InputStream retrieve(String path, Integer revision) {
+               if (!versioning)
+                       throw new ArgeoJcrException("Versioning is not activated");
+
+               try {
+                       Node fileNode = (Node) session().getItem(path);
+                       VersionHistory history = session().getWorkspace()
+                                       .getVersionManager().getVersionHistory(fileNode.getPath());
+                       int count = 0;
+                       Version version = null;
+                       for (VersionIterator it = history.getAllVersions(); it.hasNext();) {
+                               version = (Version) it.next();
+                               if (count == revision + 1) {
+                                       InputStream in = fromVersion(version);
+                                       if (log.isDebugEnabled())
+                                               log.debug("Retrieved " + path + " at revision "
+                                                               + revision);
+                                       return in;
+                               }
+                               count++;
+                       }
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot retrieve version " + revision
+                                       + " of " + path, e);
+               }
+
+               throw new ArgeoJcrException("Version " + revision
+                               + " does not exist for node " + path);
+       }
+
+       protected InputStream fromVersion(Version version)
+                       throws RepositoryException {
+               Node frozenNode = version.getNode("jcr:frozenNode");
+               InputStream in = frozenNode.getNode(Property.JCR_CONTENT)
+                               .getProperty(Property.JCR_DATA).getBinary().getStream();
+               return in;
+       }
+
+       protected Session session() {
+               return session;
+       }
+
+       public void setVersioning(Boolean versioning) {
+               this.versioning = versioning;
+       }
+
+       public void setDefaultEncoding(String defaultEncoding) {
+               this.defaultEncoding = defaultEncoding;
+       }
+
+       protected String fill(Integer number) {
+               int size = 4;
+               String str = number.toString();
+               for (int i = str.length(); i < size; i++) {
+                       str = "0" + str;
+               }
+               return str;
+       }
+
+       public void setSession(Session session) {
+               this.session = session;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/JcrUrlStreamHandler.java b/org.argeo.jcr/src/org/argeo/jcr/JcrUrlStreamHandler.java
new file mode 100644 (file)
index 0000000..a777639
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+/** URL stream handler able to deal with nt:file node and properties. NOT FINISHED */
+public class JcrUrlStreamHandler extends URLStreamHandler {
+       private final Session session;
+
+       public JcrUrlStreamHandler(Session session) {
+               this.session = session;
+       }
+
+       @Override
+       protected URLConnection openConnection(final URL u) throws IOException {
+               // TODO Auto-generated method stub
+               return new URLConnection(u) {
+
+                       @Override
+                       public void connect() throws IOException {
+                               String itemPath = u.getPath();
+                               try {
+                                       if (!session.itemExists(itemPath))
+                                               throw new IOException("No item under " + itemPath);
+
+                                       Item item = session.getItem(u.getPath());
+                                       if (item.isNode()) {
+                                               // this should be a nt:file node
+                                               Node node = (Node) item;
+                                               if (!node.getPrimaryNodeType().isNodeType(
+                                                               NodeType.NT_FILE))
+                                                       throw new IOException("Node " + node + " is not a "
+                                                                       + NodeType.NT_FILE);
+
+                                       } else {
+                                               Property property = (Property) item;
+                                               if(property.getType()==PropertyType.BINARY){
+                                                       //Binary binary = property.getBinary();
+                                                       
+                                               }
+                                       }
+                               } catch (RepositoryException e) {
+                                       IOException ioe = new IOException(
+                                                       "Unexpected JCR exception");
+                                       ioe.initCause(e);
+                                       throw ioe;
+                               }
+                       }
+
+                       @Override
+                       public InputStream getInputStream() throws IOException {
+                               // TODO Auto-generated method stub
+                               return super.getInputStream();
+                       }
+
+               };
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/JcrUtils.java b/org.argeo.jcr/src/org/argeo/jcr/JcrUtils.java
new file mode 100644 (file)
index 0000000..98a2da8
--- /dev/null
@@ -0,0 +1,1592 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.Principal;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.jcr.Binary;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.Workspace;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.observation.EventListener;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+import javax.jcr.security.AccessControlEntry;
+import javax.jcr.security.AccessControlList;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.AccessControlPolicy;
+import javax.jcr.security.AccessControlPolicyIterator;
+import javax.jcr.security.Privilege;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoMonitor;
+import org.argeo.util.security.DigestUtils;
+import org.argeo.util.security.SimplePrincipal;
+
+/** Utility methods to simplify common JCR operations. */
+public class JcrUtils implements ArgeoJcrConstants {
+
+       final private static Log log = LogFactory.getLog(JcrUtils.class);
+
+       /**
+        * Not complete yet. See
+        * http://www.day.com/specs/jcr/2.0/3_Repository_Model.html#3.2.2%20Local
+        * %20Names
+        */
+       public final static char[] INVALID_NAME_CHARACTERS = { '/', ':', '[', ']',
+                       '|', '*', /*
+                                        * invalid XML chars :
+                                        */
+                       '<', '>', '&' };
+
+       /** Prevents instantiation */
+       private JcrUtils() {
+       }
+
+       /**
+        * Queries one single node.
+        * 
+        * @return one single node or null if none was found
+        * @throws ArgeoJcrException
+        *             if more than one node was found
+        */
+       public static Node querySingleNode(Query query) {
+               NodeIterator nodeIterator;
+               try {
+                       QueryResult queryResult = query.execute();
+                       nodeIterator = queryResult.getNodes();
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot execute query " + query, e);
+               }
+               Node node;
+               if (nodeIterator.hasNext())
+                       node = nodeIterator.nextNode();
+               else
+                       return null;
+
+               if (nodeIterator.hasNext())
+                       throw new ArgeoJcrException("Query returned more than one node.");
+               return node;
+       }
+
+       /** Retrieves the node name from the provided path */
+       public static String nodeNameFromPath(String path) {
+               if (path.equals("/"))
+                       return "";
+               if (path.charAt(0) != '/')
+                       throw new ArgeoJcrException("Path " + path + " must start with a '/'");
+               String pathT = path;
+               if (pathT.charAt(pathT.length() - 1) == '/')
+                       pathT = pathT.substring(0, pathT.length() - 2);
+
+               int index = pathT.lastIndexOf('/');
+               return pathT.substring(index + 1);
+       }
+
+       /** Retrieves the parent path of the provided path */
+       public static String parentPath(String path) {
+               if (path.equals("/"))
+                       throw new ArgeoJcrException("Root path '/' has no parent path");
+               if (path.charAt(0) != '/')
+                       throw new ArgeoJcrException("Path " + path + " must start with a '/'");
+               String pathT = path;
+               if (pathT.charAt(pathT.length() - 1) == '/')
+                       pathT = pathT.substring(0, pathT.length() - 2);
+
+               int index = pathT.lastIndexOf('/');
+               return pathT.substring(0, index);
+       }
+
+       /** The provided data as a path ('/' at the end, not the beginning) */
+       public static String dateAsPath(Calendar cal) {
+               return dateAsPath(cal, false);
+       }
+
+       /**
+        * Creates a deep path based on a URL:
+        * http://subdomain.example.com/to/content?args =>
+        * com/example/subdomain/to/content
+        */
+       public static String urlAsPath(String url) {
+               try {
+                       URL u = new URL(url);
+                       StringBuffer path = new StringBuffer(url.length());
+                       // invert host
+                       path.append(hostAsPath(u.getHost()));
+                       // we don't put port since it may not always be there and may change
+                       path.append(u.getPath());
+                       return path.toString();
+               } catch (MalformedURLException e) {
+                       throw new ArgeoJcrException("Cannot generate URL path for " + url, e);
+               }
+       }
+
+       /** Set the {@link NodeType#NT_ADDRESS} properties based on this URL. */
+       public static void urlToAddressProperties(Node node, String url) {
+               try {
+                       URL u = new URL(url);
+                       node.setProperty(Property.JCR_PROTOCOL, u.getProtocol());
+                       node.setProperty(Property.JCR_HOST, u.getHost());
+                       node.setProperty(Property.JCR_PORT, Integer.toString(u.getPort()));
+                       node.setProperty(Property.JCR_PATH, normalizePath(u.getPath()));
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot set URL " + url
+                                       + " as nt:address properties", e);
+               }
+       }
+
+       /** Build URL based on the {@link NodeType#NT_ADDRESS} properties. */
+       public static String urlFromAddressProperties(Node node) {
+               try {
+                       URL u = new URL(
+                                       node.getProperty(Property.JCR_PROTOCOL).getString(), node
+                                                       .getProperty(Property.JCR_HOST).getString(),
+                                       (int) node.getProperty(Property.JCR_PORT).getLong(), node
+                                                       .getProperty(Property.JCR_PATH).getString());
+                       return u.toString();
+               } catch (Exception e) {
+                       throw new ArgeoJcrException(
+                                       "Cannot get URL from nt:address properties of " + node, e);
+               }
+       }
+
+       /*
+        * PATH UTILITIES
+        */
+
+       /** Make sure that: starts with '/', do not end with '/', do not have '//' */
+       public static String normalizePath(String path) {
+               List<String> tokens = tokenize(path);
+               StringBuffer buf = new StringBuffer(path.length());
+               for (String token : tokens) {
+                       buf.append('/');
+                       buf.append(token);
+               }
+               return buf.toString();
+       }
+
+       /**
+        * Creates a path from a FQDN, inverting the order of the component:
+        * www.argeo.org => org.argeo.www
+        */
+       public static String hostAsPath(String host) {
+               StringBuffer path = new StringBuffer(host.length());
+               String[] hostTokens = host.split("\\.");
+               for (int i = hostTokens.length - 1; i >= 0; i--) {
+                       path.append(hostTokens[i]);
+                       if (i != 0)
+                               path.append('/');
+               }
+               return path.toString();
+       }
+
+       /**
+        * Creates a path from a UUID (e.g. 6ebda899-217d-4bf1-abe4-2839085c8f3c =>
+        * 6ebda899-217d/4bf1/abe4/2839085c8f3c/). '/' at the end, not the beginning
+        */
+       public static String uuidAsPath(String uuid) {
+               StringBuffer path = new StringBuffer(uuid.length());
+               String[] tokens = uuid.split("-");
+               for (int i = 0; i < tokens.length; i++) {
+                       path.append(tokens[i]);
+                       if (i != 0)
+                               path.append('/');
+               }
+               return path.toString();
+       }
+
+       /**
+        * The provided data as a path ('/' at the end, not the beginning)
+        * 
+        * @param cal
+        *            the date
+        * @param addHour
+        *            whether to add hour as well
+        */
+       public static String dateAsPath(Calendar cal, Boolean addHour) {
+               StringBuffer buf = new StringBuffer(14);
+               buf.append('Y');
+               buf.append(cal.get(Calendar.YEAR));
+               buf.append('/');
+
+               int month = cal.get(Calendar.MONTH) + 1;
+               buf.append('M');
+               if (month < 10)
+                       buf.append(0);
+               buf.append(month);
+               buf.append('/');
+
+               int day = cal.get(Calendar.DAY_OF_MONTH);
+               buf.append('D');
+               if (day < 10)
+                       buf.append(0);
+               buf.append(day);
+               buf.append('/');
+
+               if (addHour) {
+                       int hour = cal.get(Calendar.HOUR_OF_DAY);
+                       buf.append('H');
+                       if (hour < 10)
+                               buf.append(0);
+                       buf.append(hour);
+                       buf.append('/');
+               }
+               return buf.toString();
+
+       }
+
+       /** Converts in one call a string into a gregorian calendar. */
+       public static Calendar parseCalendar(DateFormat dateFormat, String value) {
+               try {
+                       Date date = dateFormat.parse(value);
+                       Calendar calendar = new GregorianCalendar();
+                       calendar.setTime(date);
+                       return calendar;
+               } catch (ParseException e) {
+                       throw new ArgeoJcrException("Cannot parse " + value
+                                       + " with date format " + dateFormat, e);
+               }
+
+       }
+
+       /** The last element of a path. */
+       public static String lastPathElement(String path) {
+               if (path.charAt(path.length() - 1) == '/')
+                       throw new ArgeoJcrException("Path " + path + " cannot end with '/'");
+               int index = path.lastIndexOf('/');
+               if (index < 0)
+                       return path;
+               return path.substring(index + 1);
+       }
+
+       /**
+        * Call {@link Node#getName()} without exceptions (useful in super
+        * constructors).
+        */
+       public static String getNameQuietly(Node node) {
+               try {
+                       return node.getName();
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot get name from " + node, e);
+               }
+       }
+
+       /**
+        * Call {@link Node#getProperty(String)} without exceptions (useful in super
+        * constructors).
+        */
+       public static String getStringPropertyQuietly(Node node, String propertyName) {
+               try {
+                       return node.getProperty(propertyName).getString();
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot get name from " + node, e);
+               }
+       }
+
+       /**
+        * Routine that get the child with this name, adding id it does not already
+        * exist
+        */
+       public static Node getOrAdd(Node parent, String childName,
+                       String childPrimaryNodeType) throws RepositoryException {
+               return parent.hasNode(childName) ? parent.getNode(childName) : parent
+                               .addNode(childName, childPrimaryNodeType);
+       }
+
+       /**
+        * Routine that get the child with this name, adding id it does not already
+        * exist
+        */
+       public static Node getOrAdd(Node parent, String childName)
+                       throws RepositoryException {
+               return parent.hasNode(childName) ? parent.getNode(childName) : parent
+                               .addNode(childName);
+       }
+
+       /** Convert a {@link NodeIterator} to a list of {@link Node} */
+       public static List<Node> nodeIteratorToList(NodeIterator nodeIterator) {
+               List<Node> nodes = new ArrayList<Node>();
+               while (nodeIterator.hasNext()) {
+                       nodes.add(nodeIterator.nextNode());
+               }
+               return nodes;
+       }
+
+       /*
+        * PROPERTIES
+        */
+
+       /**
+        * Concisely get the string value of a property or null if this node doesn't
+        * have this property
+        */
+       public static String get(Node node, String propertyName) {
+               try {
+                       if (!node.hasProperty(propertyName))
+                               return null;
+                       return node.getProperty(propertyName).getString();
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot get property " + propertyName
+                                       + " of " + node, e);
+               }
+       }
+
+       /** Concisely get the boolean value of a property */
+       public static Boolean check(Node node, String propertyName) {
+               try {
+                       return node.getProperty(propertyName).getBoolean();
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot get property " + propertyName
+                                       + " of " + node, e);
+               }
+       }
+
+       /** Concisely get the bytes array value of a property */
+       public static byte[] getBytes(Node node, String propertyName) {
+               try {
+                       return getBinaryAsBytes(node.getProperty(propertyName));
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot get property " + propertyName
+                                       + " of " + node, e);
+               }
+       }
+
+       /** Creates the nodes making path, if they don't exist. */
+       public static Node mkdirs(Session session, String path) {
+               return mkdirs(session, path, null, null, false);
+       }
+
+       /**
+        * use {@link #mkdirs(Session, String, String, String, Boolean)} instead.
+        * 
+        * @deprecated
+        */
+       @Deprecated
+       public static Node mkdirs(Session session, String path, String type,
+                       Boolean versioning) {
+               return mkdirs(session, path, type, type, false);
+       }
+
+       /**
+        * @param type
+        *            the type of the leaf node
+        */
+       public static Node mkdirs(Session session, String path, String type) {
+               return mkdirs(session, path, type, null, false);
+       }
+
+       /**
+        * Create sub nodes relative to a parent node
+        * 
+        * @param nodeType
+        *            the type of the leaf node
+        */
+       public static Node mkdirs(Node parentNode, String relativePath,
+                       String nodeType) {
+               return mkdirs(parentNode, relativePath, nodeType, null);
+       }
+
+       /**
+        * Create sub nodes relative to a parent node
+        * 
+        * @param nodeType
+        *            the type of the leaf node
+        */
+       public static Node mkdirs(Node parentNode, String relativePath,
+                       String nodeType, String intermediaryNodeType) {
+               List<String> tokens = tokenize(relativePath);
+               Node currParent = parentNode;
+               try {
+                       for (int i = 0; i < tokens.size(); i++) {
+                               String name = tokens.get(i);
+                               if (currParent.hasNode(name)) {
+                                       currParent = currParent.getNode(name);
+                               } else {
+                                       if (i != (tokens.size() - 1)) {// intermediary
+                                               currParent = currParent.addNode(name,
+                                                               intermediaryNodeType);
+                                       } else {// leaf
+                                               currParent = currParent.addNode(name, nodeType);
+                                       }
+                               }
+                       }
+                       return currParent;
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot mkdirs relative path "
+                                       + relativePath + " from " + parentNode, e);
+               }
+       }
+
+       /**
+        * Synchronized and save is performed, to avoid race conditions in
+        * initializers leading to duplicate nodes.
+        */
+       public synchronized static Node mkdirsSafe(Session session, String path,
+                       String type) {
+               try {
+                       if (session.hasPendingChanges())
+                               throw new ArgeoJcrException(
+                                               "Session has pending changes, save them first.");
+                       Node node = mkdirs(session, path, type);
+                       session.save();
+                       return node;
+               } catch (RepositoryException e) {
+                       discardQuietly(session);
+                       throw new ArgeoJcrException("Cannot safely make directories", e);
+               }
+       }
+
+       public synchronized static Node mkdirsSafe(Session session, String path) {
+               return mkdirsSafe(session, path, null);
+       }
+
+       /**
+        * Creates the nodes making path, if they don't exist. This is up to the
+        * caller to save the session. Use with caution since it can create
+        * duplicate nodes if used concurrently.
+        */
+       public static Node mkdirs(Session session, String path, String type,
+                       String intermediaryNodeType, Boolean versioning) {
+               try {
+                       if (path.equals('/'))
+                               return session.getRootNode();
+
+                       if (session.itemExists(path)) {
+                               Node node = session.getNode(path);
+                               // check type
+                               if (type != null && !node.isNodeType(type)
+                                               && !node.getPath().equals("/"))
+                                       throw new ArgeoJcrException("Node " + node
+                                                       + " exists but is of type "
+                                                       + node.getPrimaryNodeType().getName()
+                                                       + " not of type " + type);
+                               // TODO: check versioning
+                               return node;
+                       }
+
+                       StringBuffer current = new StringBuffer("/");
+                       Node currentNode = session.getRootNode();
+                       Iterator<String> it = tokenize(path).iterator();
+                       while (it.hasNext()) {
+                               String part = it.next();
+                               current.append(part).append('/');
+                               if (!session.itemExists(current.toString())) {
+                                       if (!it.hasNext() && type != null)
+                                               currentNode = currentNode.addNode(part, type);
+                                       else if (it.hasNext() && intermediaryNodeType != null)
+                                               currentNode = currentNode.addNode(part,
+                                                               intermediaryNodeType);
+                                       else
+                                               currentNode = currentNode.addNode(part);
+                                       if (versioning)
+                                               currentNode.addMixin(NodeType.MIX_VERSIONABLE);
+                                       if (log.isTraceEnabled())
+                                               log.debug("Added folder " + part + " as " + current);
+                               } else {
+                                       currentNode = (Node) session.getItem(current.toString());
+                               }
+                       }
+                       return currentNode;
+               } catch (RepositoryException e) {
+                       discardQuietly(session);
+                       throw new ArgeoJcrException("Cannot mkdirs " + path, e);
+               } finally {
+               }
+       }
+
+       /** Convert a path to the list of its tokens */
+       public static List<String> tokenize(String path) {
+               List<String> tokens = new ArrayList<String>();
+               boolean optimized = false;
+               if (!optimized) {
+                       String[] rawTokens = path.split("/");
+                       for (String token : rawTokens) {
+                               if (!token.equals(""))
+                                       tokens.add(token);
+                       }
+               } else {
+                       StringBuffer curr = new StringBuffer();
+                       char[] arr = path.toCharArray();
+                       chars: for (int i = 0; i < arr.length; i++) {
+                               char c = arr[i];
+                               if (c == '/') {
+                                       if (i == 0 || (i == arr.length - 1))
+                                               continue chars;
+                                       if (curr.length() > 0) {
+                                               tokens.add(curr.toString());
+                                               curr = new StringBuffer();
+                                       }
+                               } else
+                                       curr.append(c);
+                       }
+                       if (curr.length() > 0) {
+                               tokens.add(curr.toString());
+                               curr = new StringBuffer();
+                       }
+               }
+               return Collections.unmodifiableList(tokens);
+       }
+
+       /**
+        * Safe and repository implementation independent registration of a
+        * namespace.
+        */
+       public static void registerNamespaceSafely(Session session, String prefix,
+                       String uri) {
+               try {
+                       registerNamespaceSafely(session.getWorkspace()
+                                       .getNamespaceRegistry(), prefix, uri);
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot find namespace registry", e);
+               }
+       }
+
+       /**
+        * Safe and repository implementation independent registration of a
+        * namespace.
+        */
+       public static void registerNamespaceSafely(NamespaceRegistry nr,
+                       String prefix, String uri) {
+               try {
+                       String[] prefixes = nr.getPrefixes();
+                       for (String pref : prefixes)
+                               if (pref.equals(prefix)) {
+                                       String registeredUri = nr.getURI(pref);
+                                       if (!registeredUri.equals(uri))
+                                               throw new ArgeoJcrException("Prefix " + pref
+                                                               + " already registered for URI "
+                                                               + registeredUri
+                                                               + " which is different from provided URI "
+                                                               + uri);
+                                       else
+                                               return;// skip
+                               }
+                       nr.registerNamespace(prefix, uri);
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot register namespace " + uri
+                                       + " under prefix " + prefix, e);
+               }
+       }
+
+       /** Recursively outputs the contents of the given node. */
+       public static void debug(Node node) {
+               debug(node, log);
+       }
+
+       /** Recursively outputs the contents of the given node. */
+       public static void debug(Node node, Log log) {
+               try {
+                       // First output the node path
+                       log.debug(node.getPath());
+                       // Skip the virtual (and large!) jcr:system subtree
+                       if (node.getName().equals("jcr:system")) {
+                               return;
+                       }
+
+                       // Then the children nodes (recursive)
+                       NodeIterator it = node.getNodes();
+                       while (it.hasNext()) {
+                               Node childNode = it.nextNode();
+                               debug(childNode, log);
+                       }
+
+                       // Then output the properties
+                       PropertyIterator properties = node.getProperties();
+                       // log.debug("Property are : ");
+
+                       properties: while (properties.hasNext()) {
+                               Property property = properties.nextProperty();
+                               if (property.getType() == PropertyType.BINARY)
+                                       continue properties;// skip
+                               if (property.getDefinition().isMultiple()) {
+                                       // A multi-valued property, print all values
+                                       Value[] values = property.getValues();
+                                       for (int i = 0; i < values.length; i++) {
+                                               log.debug(property.getPath() + "="
+                                                               + values[i].getString());
+                                       }
+                               } else {
+                                       // A single-valued property
+                                       log.debug(property.getPath() + "=" + property.getString());
+                               }
+                       }
+               } catch (Exception e) {
+                       log.error("Could not debug " + node, e);
+               }
+
+       }
+
+       /** Logs the effective access control policies */
+       public static void logEffectiveAccessPolicies(Node node) {
+               try {
+                       logEffectiveAccessPolicies(node.getSession(), node.getPath());
+               } catch (RepositoryException e) {
+                       log.error("Cannot log effective access policies of " + node, e);
+               }
+       }
+
+       /** Logs the effective access control policies */
+       public static void logEffectiveAccessPolicies(Session session, String path) {
+               if (!log.isDebugEnabled())
+                       return;
+
+               try {
+                       AccessControlPolicy[] effectivePolicies = session
+                                       .getAccessControlManager().getEffectivePolicies(path);
+                       if (effectivePolicies.length > 0) {
+                               for (AccessControlPolicy policy : effectivePolicies) {
+                                       if (policy instanceof AccessControlList) {
+                                               AccessControlList acl = (AccessControlList) policy;
+                                               log.debug("Access control list for " + path + "\n"
+                                                               + accessControlListSummary(acl));
+                                       }
+                               }
+                       } else {
+                               log.debug("No effective access control policy for " + path);
+                       }
+               } catch (RepositoryException e) {
+                       log.error("Cannot log effective access policies of " + path, e);
+               }
+       }
+
+       /** Returns a human-readable summary of this access control list. */
+       public static String accessControlListSummary(AccessControlList acl) {
+               StringBuffer buf = new StringBuffer("");
+               try {
+                       for (AccessControlEntry ace : acl.getAccessControlEntries()) {
+                               buf.append('\t').append(ace.getPrincipal().getName())
+                                               .append('\n');
+                               for (Privilege priv : ace.getPrivileges())
+                                       buf.append("\t\t").append(priv.getName()).append('\n');
+                       }
+                       return buf.toString();
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot write summary of " + acl, e);
+               }
+       }
+
+       /**
+        * Copies recursively the content of a node to another one. Do NOT copy the
+        * property values of {@link NodeType#MIX_CREATED} and
+        * {@link NodeType#MIX_LAST_MODIFIED}, but update the
+        * {@link Property#JCR_LAST_MODIFIED} and
+        * {@link Property#JCR_LAST_MODIFIED_BY} properties if the target node has
+        * the {@link NodeType#MIX_LAST_MODIFIED} mixin.
+        */
+       public static void copy(Node fromNode, Node toNode) {
+               try {
+                       if (toNode.getDefinition().isProtected())
+                               return;
+
+                       // process properties
+                       PropertyIterator pit = fromNode.getProperties();
+                       properties: while (pit.hasNext()) {
+                               Property fromProperty = pit.nextProperty();
+                               String propertyName = fromProperty.getName();
+                               if (toNode.hasProperty(propertyName)
+                                               && toNode.getProperty(propertyName).getDefinition()
+                                                               .isProtected())
+                                       continue properties;
+
+                               if (fromProperty.getDefinition().isProtected())
+                                       continue properties;
+
+                               if (propertyName.equals("jcr:created")
+                                               || propertyName.equals("jcr:createdBy")
+                                               || propertyName.equals("jcr:lastModified")
+                                               || propertyName.equals("jcr:lastModifiedBy"))
+                                       continue properties;
+
+                               if (fromProperty.isMultiple()) {
+                                       toNode.setProperty(propertyName, fromProperty.getValues());
+                               } else {
+                                       toNode.setProperty(propertyName, fromProperty.getValue());
+                               }
+                       }
+
+                       // update jcr:lastModified and jcr:lastModifiedBy in toNode in case
+                       // they existed, before adding the mixins
+                       updateLastModified(toNode);
+
+                       // add mixins
+                       for (NodeType mixinType : fromNode.getMixinNodeTypes()) {
+                               toNode.addMixin(mixinType.getName());
+                       }
+
+                       // process children nodes
+                       NodeIterator nit = fromNode.getNodes();
+                       while (nit.hasNext()) {
+                               Node fromChild = nit.nextNode();
+                               Integer index = fromChild.getIndex();
+                               String nodeRelPath = fromChild.getName() + "[" + index + "]";
+                               Node toChild;
+                               if (toNode.hasNode(nodeRelPath))
+                                       toChild = toNode.getNode(nodeRelPath);
+                               else
+                                       toChild = toNode.addNode(fromChild.getName(), fromChild
+                                                       .getPrimaryNodeType().getName());
+                               copy(fromChild, toChild);
+                       }
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot copy " + fromNode + " to "
+                                       + toNode, e);
+               }
+       }
+
+       /**
+        * Check whether all first-level properties (except jcr:* properties) are
+        * equal. Skip jcr:* properties
+        */
+       public static Boolean allPropertiesEquals(Node reference, Node observed,
+                       Boolean onlyCommonProperties) {
+               try {
+                       PropertyIterator pit = reference.getProperties();
+                       props: while (pit.hasNext()) {
+                               Property propReference = pit.nextProperty();
+                               String propName = propReference.getName();
+                               if (propName.startsWith("jcr:"))
+                                       continue props;
+
+                               if (!observed.hasProperty(propName))
+                                       if (onlyCommonProperties)
+                                               continue props;
+                                       else
+                                               return false;
+                               // TODO: deal with multiple property values?
+                               if (!observed.getProperty(propName).getValue()
+                                               .equals(propReference.getValue()))
+                                       return false;
+                       }
+                       return true;
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot check all properties equals of "
+                                       + reference + " and " + observed, e);
+               }
+       }
+
+       public static Map<String, PropertyDiff> diffProperties(Node reference,
+                       Node observed) {
+               Map<String, PropertyDiff> diffs = new TreeMap<String, PropertyDiff>();
+               diffPropertiesLevel(diffs, null, reference, observed);
+               return diffs;
+       }
+
+       /**
+        * Compare the properties of two nodes. Recursivity to child nodes is not
+        * yet supported. Skip jcr:* properties.
+        */
+       static void diffPropertiesLevel(Map<String, PropertyDiff> diffs,
+                       String baseRelPath, Node reference, Node observed) {
+               try {
+                       // check removed and modified
+                       PropertyIterator pit = reference.getProperties();
+                       props: while (pit.hasNext()) {
+                               Property p = pit.nextProperty();
+                               String name = p.getName();
+                               if (name.startsWith("jcr:"))
+                                       continue props;
+
+                               if (!observed.hasProperty(name)) {
+                                       String relPath = propertyRelPath(baseRelPath, name);
+                                       PropertyDiff pDiff = new PropertyDiff(PropertyDiff.REMOVED,
+                                                       relPath, p.getValue(), null);
+                                       diffs.put(relPath, pDiff);
+                               } else {
+                                       if (p.isMultiple()) {
+                                               // FIXME implement multiple
+                                       } else {
+                                               Value referenceValue = p.getValue();
+                                               Value newValue = observed.getProperty(name).getValue();
+                                               if (!referenceValue.equals(newValue)) {
+                                                       String relPath = propertyRelPath(baseRelPath, name);
+                                                       PropertyDiff pDiff = new PropertyDiff(
+                                                                       PropertyDiff.MODIFIED, relPath,
+                                                                       referenceValue, newValue);
+                                                       diffs.put(relPath, pDiff);
+                                               }
+                                       }
+                               }
+                       }
+                       // check added
+                       pit = observed.getProperties();
+                       props: while (pit.hasNext()) {
+                               Property p = pit.nextProperty();
+                               String name = p.getName();
+                               if (name.startsWith("jcr:"))
+                                       continue props;
+                               if (!reference.hasProperty(name)) {
+                                       if (p.isMultiple()) {
+                                               // FIXME implement multiple
+                                       } else {
+                                               String relPath = propertyRelPath(baseRelPath, name);
+                                               PropertyDiff pDiff = new PropertyDiff(
+                                                               PropertyDiff.ADDED, relPath, null, p.getValue());
+                                               diffs.put(relPath, pDiff);
+                                       }
+                               }
+                       }
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot diff " + reference + " and "
+                                       + observed, e);
+               }
+       }
+
+       /**
+        * Compare only a restricted list of properties of two nodes. No
+        * recursivity.
+        * 
+        */
+       public static Map<String, PropertyDiff> diffProperties(Node reference,
+                       Node observed, List<String> properties) {
+               Map<String, PropertyDiff> diffs = new TreeMap<String, PropertyDiff>();
+               try {
+                       Iterator<String> pit = properties.iterator();
+
+                       props: while (pit.hasNext()) {
+                               String name = pit.next();
+                               if (!reference.hasProperty(name)) {
+                                       if (!observed.hasProperty(name))
+                                               continue props;
+                                       Value val = observed.getProperty(name).getValue();
+                                       try {
+                                               // empty String but not null
+                                               if ("".equals(val.getString()))
+                                                       continue props;
+                                       } catch (Exception e) {
+                                               // not parseable as String, silent
+                                       }
+                                       PropertyDiff pDiff = new PropertyDiff(PropertyDiff.ADDED,
+                                                       name, null, val);
+                                       diffs.put(name, pDiff);
+                               } else if (!observed.hasProperty(name)) {
+                                       PropertyDiff pDiff = new PropertyDiff(PropertyDiff.REMOVED,
+                                                       name, reference.getProperty(name).getValue(), null);
+                                       diffs.put(name, pDiff);
+                               } else {
+                                       Value referenceValue = reference.getProperty(name)
+                                                       .getValue();
+                                       Value newValue = observed.getProperty(name).getValue();
+                                       if (!referenceValue.equals(newValue)) {
+                                               PropertyDiff pDiff = new PropertyDiff(
+                                                               PropertyDiff.MODIFIED, name, referenceValue,
+                                                               newValue);
+                                               diffs.put(name, pDiff);
+                                       }
+                               }
+                       }
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot diff " + reference + " and "
+                                       + observed, e);
+               }
+               return diffs;
+       }
+
+       /** Builds a property relPath to be used in the diff. */
+       private static String propertyRelPath(String baseRelPath,
+                       String propertyName) {
+               if (baseRelPath == null)
+                       return propertyName;
+               else
+                       return baseRelPath + '/' + propertyName;
+       }
+
+       /**
+        * Normalizes a name so that it can be stored in contexts not supporting
+        * names with ':' (typically databases). Replaces ':' by '_'.
+        */
+       public static String normalize(String name) {
+               return name.replace(':', '_');
+       }
+
+       /**
+        * Replaces characters which are invalid in a JCR name by '_'. Currently not
+        * exhaustive.
+        * 
+        * @see JcrUtils#INVALID_NAME_CHARACTERS
+        */
+       public static String replaceInvalidChars(String name) {
+               return replaceInvalidChars(name, '_');
+       }
+
+       /**
+        * Replaces characters which are invalid in a JCR name. Currently not
+        * exhaustive.
+        * 
+        * @see JcrUtils#INVALID_NAME_CHARACTERS
+        */
+       public static String replaceInvalidChars(String name, char replacement) {
+               boolean modified = false;
+               char[] arr = name.toCharArray();
+               for (int i = 0; i < arr.length; i++) {
+                       char c = arr[i];
+                       invalid: for (char invalid : INVALID_NAME_CHARACTERS) {
+                               if (c == invalid) {
+                                       arr[i] = replacement;
+                                       modified = true;
+                                       break invalid;
+                               }
+                       }
+               }
+               if (modified)
+                       return new String(arr);
+               else
+                       // do not create new object if unnecessary
+                       return name;
+       }
+
+       /**
+        * Removes forbidden characters from a path, replacing them with '_'
+        * 
+        * @deprecated use {@link #replaceInvalidChars(String)} instead
+        */
+       public static String removeForbiddenCharacters(String str) {
+               return str.replace('[', '_').replace(']', '_').replace('/', '_')
+                               .replace('*', '_');
+
+       }
+
+       /** Cleanly disposes a {@link Binary} even if it is null. */
+       public static void closeQuietly(Binary binary) {
+               if (binary == null)
+                       return;
+               binary.dispose();
+       }
+
+       /** Retrieve a {@link Binary} as a byte array */
+       public static byte[] getBinaryAsBytes(Property property) {
+               ByteArrayOutputStream out = new ByteArrayOutputStream();
+               InputStream in = null;
+               Binary binary = null;
+               try {
+                       binary = property.getBinary();
+                       in = binary.getStream();
+                       IOUtils.copy(in, out);
+                       return out.toByteArray();
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot read binary " + property
+                                       + " as bytes", e);
+               } finally {
+                       IOUtils.closeQuietly(out);
+                       IOUtils.closeQuietly(in);
+                       closeQuietly(binary);
+               }
+       }
+
+       /** Writes a {@link Binary} from a byte array */
+       public static void setBinaryAsBytes(Node node, String property, byte[] bytes) {
+               InputStream in = null;
+               Binary binary = null;
+               try {
+                       in = new ByteArrayInputStream(bytes);
+                       binary = node.getSession().getValueFactory().createBinary(in);
+                       node.setProperty(property, binary);
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot read binary " + property
+                                       + " as bytes", e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+                       closeQuietly(binary);
+               }
+       }
+
+       /**
+        * Creates depth from a string (typically a username) by adding levels based
+        * on its first characters: "aBcD",2 => a/aB
+        */
+       public static String firstCharsToPath(String str, Integer nbrOfChars) {
+               if (str.length() < nbrOfChars)
+                       throw new ArgeoJcrException("String " + str
+                                       + " length must be greater or equal than " + nbrOfChars);
+               StringBuffer path = new StringBuffer("");
+               StringBuffer curr = new StringBuffer("");
+               for (int i = 0; i < nbrOfChars; i++) {
+                       curr.append(str.charAt(i));
+                       path.append(curr);
+                       if (i < nbrOfChars - 1)
+                               path.append('/');
+               }
+               return path.toString();
+       }
+
+       /**
+        * Discards the current changes in the session attached to this node. To be
+        * used typically in a catch block.
+        * 
+        * @see #discardQuietly(Session)
+        */
+       public static void discardUnderlyingSessionQuietly(Node node) {
+               try {
+                       discardQuietly(node.getSession());
+               } catch (RepositoryException e) {
+                       log.warn("Cannot quietly discard session of node " + node + ": "
+                                       + e.getMessage());
+               }
+       }
+
+       /**
+        * Discards the current changes in a session by calling
+        * {@link Session#refresh(boolean)} with <code>false</code>, only logging
+        * potential errors when doing so. To be used typically in a catch block.
+        */
+       public static void discardQuietly(Session session) {
+               try {
+                       if (session != null)
+                               session.refresh(false);
+               } catch (RepositoryException e) {
+                       log.warn("Cannot quietly discard session " + session + ": "
+                                       + e.getMessage());
+               }
+       }
+
+       /**
+        * Login to a workspace with implicit credentials, creates the workspace
+        * with these credentials if it does not already exist.
+        */
+       public static Session loginOrCreateWorkspace(Repository repository,
+                       String workspaceName) throws RepositoryException {
+               Session workspaceSession = null;
+               Session defaultSession = null;
+               try {
+                       try {
+                               workspaceSession = repository.login(workspaceName);
+                       } catch (NoSuchWorkspaceException e) {
+                               // try to create workspace
+                               defaultSession = repository.login();
+                               defaultSession.getWorkspace().createWorkspace(workspaceName);
+                               workspaceSession = repository.login(workspaceName);
+                       }
+                       return workspaceSession;
+               } finally {
+                       logoutQuietly(defaultSession);
+               }
+       }
+
+       /** Logs out the session, not throwing any exception, even if it is null. */
+       public static void logoutQuietly(Session session) {
+               try {
+                       if (session != null)
+                               if (session.isLive())
+                                       session.logout();
+               } catch (Exception e) {
+                       // silent
+               }
+       }
+
+       /**
+        * Convenient method to add a listener. uuids passed as null, deep=true,
+        * local=true, only one node type
+        */
+       public static void addListener(Session session, EventListener listener,
+                       int eventTypes, String basePath, String nodeType) {
+               try {
+                       session.getWorkspace()
+                                       .getObservationManager()
+                                       .addEventListener(
+                                                       listener,
+                                                       eventTypes,
+                                                       basePath,
+                                                       true,
+                                                       null,
+                                                       nodeType == null ? null : new String[] { nodeType },
+                                                       true);
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot add JCR listener " + listener
+                                       + " to session " + session, e);
+               }
+       }
+
+       /** Removes a listener without throwing exception */
+       public static void removeListenerQuietly(Session session,
+                       EventListener listener) {
+               if (session == null || !session.isLive())
+                       return;
+               try {
+                       session.getWorkspace().getObservationManager()
+                                       .removeEventListener(listener);
+               } catch (RepositoryException e) {
+                       // silent
+               }
+       }
+
+       /**
+        * Quietly unregisters an {@link EventListener} from the udnerlying
+        * workspace of this node.
+        */
+       public static void unregisterQuietly(Node node, EventListener eventListener) {
+               try {
+                       unregisterQuietly(node.getSession().getWorkspace(), eventListener);
+               } catch (RepositoryException e) {
+                       // silent
+                       if (log.isTraceEnabled())
+                               log.trace("Could not unregister event listener "
+                                               + eventListener);
+               }
+       }
+
+       /** Quietly unregisters an {@link EventListener} from this workspace */
+       public static void unregisterQuietly(Workspace workspace,
+                       EventListener eventListener) {
+               if (eventListener == null)
+                       return;
+               try {
+                       workspace.getObservationManager()
+                                       .removeEventListener(eventListener);
+               } catch (RepositoryException e) {
+                       // silent
+                       if (log.isTraceEnabled())
+                               log.trace("Could not unregister event listener "
+                                               + eventListener);
+               }
+       }
+
+       /**
+        * If this node is has the {@link NodeType#MIX_LAST_MODIFIED} mixin, it
+        * updates the {@link Property#JCR_LAST_MODIFIED} property with the current
+        * time and the {@link Property#JCR_LAST_MODIFIED_BY} property with the
+        * underlying session user id. In Jackrabbit 2.x, <a
+        * href="https://issues.apache.org/jira/browse/JCR-2233">these properties
+        * are not automatically updated</a>, hence the need for manual update. The
+        * session is not saved.
+        */
+       public static void updateLastModified(Node node) {
+               try {
+                       if (!node.isNodeType(NodeType.MIX_LAST_MODIFIED))
+                               node.addMixin(NodeType.MIX_LAST_MODIFIED);
+                       node.setProperty(Property.JCR_LAST_MODIFIED,
+                                       new GregorianCalendar());
+                       node.setProperty(Property.JCR_LAST_MODIFIED_BY, node.getSession()
+                                       .getUserID());
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot update last modified on " + node,
+                                       e);
+               }
+       }
+
+       /**
+        * Update lastModified recursively until this parent.
+        * 
+        * @param node
+        *            the node
+        * @param untilPath
+        *            the base path, null is equivalent to "/"
+        */
+       public static void updateLastModifiedAndParents(Node node, String untilPath) {
+               try {
+                       if (untilPath != null && !node.getPath().startsWith(untilPath))
+                               throw new ArgeoJcrException(node + " is not under " + untilPath);
+                       updateLastModified(node);
+                       if (untilPath == null) {
+                               if (!node.getPath().equals("/"))
+                                       updateLastModifiedAndParents(node.getParent(), untilPath);
+                       } else {
+                               if (!node.getPath().equals(untilPath))
+                                       updateLastModifiedAndParents(node.getParent(), untilPath);
+                       }
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot update lastModified from " + node
+                                       + " until " + untilPath, e);
+               }
+       }
+
+       /**
+        * Returns a String representing the short version (see <a
+        * href="http://jackrabbit.apache.org/node-type-notation.html"> Node type
+        * Notation </a> attributes grammar) of the main business attributes of this
+        * property definition
+        * 
+        * @param prop
+        */
+       public static String getPropertyDefinitionAsString(Property prop) {
+               StringBuffer sbuf = new StringBuffer();
+               try {
+                       if (prop.getDefinition().isAutoCreated())
+                               sbuf.append("a");
+                       if (prop.getDefinition().isMandatory())
+                               sbuf.append("m");
+                       if (prop.getDefinition().isProtected())
+                               sbuf.append("p");
+                       if (prop.getDefinition().isMultiple())
+                               sbuf.append("*");
+               } catch (RepositoryException re) {
+                       throw new ArgeoJcrException(
+                                       "unexpected error while getting property definition as String",
+                                       re);
+               }
+               return sbuf.toString();
+       }
+
+       /**
+        * Estimate the sub tree size from current node. Computation is based on the
+        * Jcr {@link Property.getLength()} method. Note : it is not the exact size
+        * used on the disk by the current part of the JCR Tree.
+        */
+
+       public static long getNodeApproxSize(Node node) {
+               long curNodeSize = 0;
+               try {
+                       PropertyIterator pi = node.getProperties();
+                       while (pi.hasNext()) {
+                               Property prop = pi.nextProperty();
+                               if (prop.isMultiple()) {
+                                       int nb = prop.getLengths().length;
+                                       for (int i = 0; i < nb; i++) {
+                                               curNodeSize += (prop.getLengths()[i] > 0 ? prop
+                                                               .getLengths()[i] : 0);
+                                       }
+                               } else
+                                       curNodeSize += (prop.getLength() > 0 ? prop.getLength() : 0);
+                       }
+
+                       NodeIterator ni = node.getNodes();
+                       while (ni.hasNext())
+                               curNodeSize += getNodeApproxSize(ni.nextNode());
+                       return curNodeSize;
+               } catch (RepositoryException re) {
+                       throw new ArgeoJcrException(
+                                       "Unexpected error while recursively determining node size.",
+                                       re);
+               }
+       }
+
+       /*
+        * SECURITY
+        */
+
+       /**
+        * Convenience method for adding a single privilege to a principal (user or
+        * role), typically jcr:all
+        */
+       public synchronized static void addPrivilege(Session session, String path,
+                       String principal, String privilege) throws RepositoryException {
+               List<Privilege> privileges = new ArrayList<Privilege>();
+               privileges.add(session.getAccessControlManager().privilegeFromName(
+                               privilege));
+               addPrivileges(session, path, new SimplePrincipal(principal), privileges);
+       }
+
+       /**
+        * Add privileges on a path to a {@link Principal}. The path must already
+        * exist. Session is saved. Synchronized to prevent concurrent modifications
+        * of the same node.
+        */
+       public synchronized static Boolean addPrivileges(Session session,
+                       String path, Principal principal, List<Privilege> privs)
+                       throws RepositoryException {
+               // make sure the session is in line with the persisted state
+               session.refresh(false);
+               AccessControlManager acm = session.getAccessControlManager();
+               AccessControlList acl = getAccessControlList(acm, path);
+
+               accessControlEntries: for (AccessControlEntry ace : acl
+                               .getAccessControlEntries()) {
+                       Principal currentPrincipal = ace.getPrincipal();
+                       if (currentPrincipal.getName().equals(principal.getName())) {
+                               Privilege[] currentPrivileges = ace.getPrivileges();
+                               if (currentPrivileges.length != privs.size())
+                                       break accessControlEntries;
+                               for (int i = 0; i < currentPrivileges.length; i++) {
+                                       Privilege currP = currentPrivileges[i];
+                                       Privilege p = privs.get(i);
+                                       if (!currP.getName().equals(p.getName())) {
+                                               break accessControlEntries;
+                                       }
+                               }
+                               return false;
+                       }
+               }
+
+               Privilege[] privileges = privs.toArray(new Privilege[privs.size()]);
+               acl.addAccessControlEntry(principal, privileges);
+               acm.setPolicy(path, acl);
+               if (log.isDebugEnabled()) {
+                       StringBuffer privBuf = new StringBuffer();
+                       for (Privilege priv : privs)
+                               privBuf.append(priv.getName());
+                       log.debug("Added privileges " + privBuf + " to "
+                                       + principal.getName() + " on " + path + " in '"
+                                       + session.getWorkspace().getName() + "'");
+               }
+               session.refresh(true);
+               session.save();
+               return true;
+       }
+
+       /** Gets access control list for this path, throws exception if not found */
+       public synchronized static AccessControlList getAccessControlList(
+                       AccessControlManager acm, String path) throws RepositoryException {
+               // search for an access control list
+               AccessControlList acl = null;
+               AccessControlPolicyIterator policyIterator = acm
+                               .getApplicablePolicies(path);
+               if (policyIterator.hasNext()) {
+                       while (policyIterator.hasNext()) {
+                               AccessControlPolicy acp = policyIterator
+                                               .nextAccessControlPolicy();
+                               if (acp instanceof AccessControlList)
+                                       acl = ((AccessControlList) acp);
+                       }
+               } else {
+                       AccessControlPolicy[] existingPolicies = acm.getPolicies(path);
+                       for (AccessControlPolicy acp : existingPolicies) {
+                               if (acp instanceof AccessControlList)
+                                       acl = ((AccessControlList) acp);
+                       }
+               }
+               if (acl != null)
+                       return acl;
+               else
+                       throw new ArgeoJcrException("ACL not found at " + path);
+       }
+
+       /** Clear authorizations for a user at this path */
+       public synchronized static void clearAccessControList(Session session,
+                       String path, String username) throws RepositoryException {
+               AccessControlManager acm = session.getAccessControlManager();
+               AccessControlList acl = getAccessControlList(acm, path);
+               for (AccessControlEntry ace : acl.getAccessControlEntries()) {
+                       if (ace.getPrincipal().getName().equals(username)) {
+                               acl.removeAccessControlEntry(ace);
+                       }
+               }
+               // the new access control list must be applied otherwise this call:
+               // acl.removeAccessControlEntry(ace); has no effect
+               acm.setPolicy(path, acl);
+       }
+
+       /*
+        * FILES UTILITIES
+        */
+       /**
+        * Creates the nodes making the path as {@link NodeType#NT_FOLDER}
+        */
+       public static Node mkfolders(Session session, String path) {
+               return mkdirs(session, path, NodeType.NT_FOLDER, NodeType.NT_FOLDER,
+                               false);
+       }
+
+       /**
+        * Copy only nt:folder and nt:file, without their additional types and
+        * properties.
+        * 
+        * @param recursive
+        *            if true copies folders as well, otherwise only first level
+        *            files
+        * @return how many files were copied
+        */
+       @SuppressWarnings("deprecation")
+       public static Long copyFiles(Node fromNode, Node toNode, Boolean recursive,
+                       ArgeoMonitor monitor) {
+               long count = 0l;
+
+               Binary binary = null;
+               InputStream in = null;
+               try {
+                       NodeIterator fromChildren = fromNode.getNodes();
+                       while (fromChildren.hasNext()) {
+                               if (monitor != null && monitor.isCanceled())
+                                       throw new ArgeoJcrException(
+                                                       "Copy cancelled before it was completed");
+
+                               Node fromChild = fromChildren.nextNode();
+                               String fileName = fromChild.getName();
+                               if (fromChild.isNodeType(NodeType.NT_FILE)) {
+                                       if (monitor != null)
+                                               monitor.subTask("Copy " + fileName);
+                                       binary = fromChild.getNode(Node.JCR_CONTENT)
+                                                       .getProperty(Property.JCR_DATA).getBinary();
+                                       in = binary.getStream();
+                                       copyStreamAsFile(toNode, fileName, in);
+                                       IOUtils.closeQuietly(in);
+                                       closeQuietly(binary);
+
+                                       // save session
+                                       toNode.getSession().save();
+                                       count++;
+
+                                       if (log.isDebugEnabled())
+                                               log.debug("Copied file " + fromChild.getPath());
+                                       if (monitor != null)
+                                               monitor.worked(1);
+                               } else if (fromChild.isNodeType(NodeType.NT_FOLDER)
+                                               && recursive) {
+                                       Node toChildFolder;
+                                       if (toNode.hasNode(fileName)) {
+                                               toChildFolder = toNode.getNode(fileName);
+                                               if (!toChildFolder.isNodeType(NodeType.NT_FOLDER))
+                                                       throw new ArgeoJcrException(toChildFolder
+                                                                       + " is not of type nt:folder");
+                                       } else {
+                                               toChildFolder = toNode.addNode(fileName,
+                                                               NodeType.NT_FOLDER);
+
+                                               // save session
+                                               toNode.getSession().save();
+                                       }
+                                       count = count
+                                                       + copyFiles(fromChild, toChildFolder, recursive,
+                                                                       monitor);
+                               }
+                       }
+                       return count;
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot copy files between " + fromNode
+                                       + " and " + toNode);
+               } finally {
+                       // in case there was an exception
+                       IOUtils.closeQuietly(in);
+                       closeQuietly(binary);
+               }
+       }
+
+       /**
+        * Iteratively count all file nodes in subtree, inefficient but can be
+        * useful when query are poorly supported, such as in remoting.
+        */
+       public static Long countFiles(Node node) {
+               Long localCount = 0l;
+               try {
+                       for (NodeIterator nit = node.getNodes(); nit.hasNext();) {
+                               Node child = nit.nextNode();
+                               if (child.isNodeType(NodeType.NT_FOLDER))
+                                       localCount = localCount + countFiles(child);
+                               else if (child.isNodeType(NodeType.NT_FILE))
+                                       localCount = localCount + 1;
+                       }
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot count all children of " + node);
+               }
+               return localCount;
+       }
+
+       /**
+        * Copy a file as an nt:file, assuming an nt:folder hierarchy. The session
+        * is NOT saved.
+        * 
+        * @return the created file node
+        */
+       public static Node copyFile(Node folderNode, File file) {
+               InputStream in = null;
+               try {
+                       in = new FileInputStream(file);
+                       return copyStreamAsFile(folderNode, file.getName(), in);
+               } catch (IOException e) {
+                       throw new ArgeoJcrException("Cannot copy file " + file + " under "
+                                       + folderNode, e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+               }
+       }
+
+       /** Copy bytes as an nt:file */
+       public static Node copyBytesAsFile(Node folderNode, String fileName,
+                       byte[] bytes) {
+               InputStream in = null;
+               try {
+                       in = new ByteArrayInputStream(bytes);
+                       return copyStreamAsFile(folderNode, fileName, in);
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot copy file " + fileName + " under "
+                                       + folderNode, e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+               }
+       }
+
+       /**
+        * Copy a stream as an nt:file, assuming an nt:folder hierarchy. The session
+        * is NOT saved.
+        * 
+        * @return the created file node
+        */
+       public static Node copyStreamAsFile(Node folderNode, String fileName,
+                       InputStream in) {
+               Binary binary = null;
+               try {
+                       Node fileNode;
+                       Node contentNode;
+                       if (folderNode.hasNode(fileName)) {
+                               fileNode = folderNode.getNode(fileName);
+                               if (!fileNode.isNodeType(NodeType.NT_FILE))
+                                       throw new ArgeoJcrException(fileNode
+                                                       + " is not of type nt:file");
+                               // we assume that the content node is already there
+                               contentNode = fileNode.getNode(Node.JCR_CONTENT);
+                       } else {
+                               fileNode = folderNode.addNode(fileName, NodeType.NT_FILE);
+                               contentNode = fileNode.addNode(Node.JCR_CONTENT,
+                                               NodeType.NT_RESOURCE);
+                       }
+                       binary = contentNode.getSession().getValueFactory()
+                                       .createBinary(in);
+                       contentNode.setProperty(Property.JCR_DATA, binary);
+                       return fileNode;
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot create file node " + fileName
+                                       + " under " + folderNode, e);
+               } finally {
+                       closeQuietly(binary);
+               }
+       }
+
+       /** Computes the checksum of an nt:file */
+       public static String checksumFile(Node fileNode, String algorithm) {
+               Binary data = null;
+               InputStream in = null;
+               try {
+                       data = fileNode.getNode(Node.JCR_CONTENT)
+                                       .getProperty(Property.JCR_DATA).getBinary();
+                       in = data.getStream();
+                       return DigestUtils.digest(algorithm, in);
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot checksum file " + fileNode, e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+                       closeQuietly(data);
+               }
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/PropertyDiff.java b/org.argeo.jcr/src/org/argeo/jcr/PropertyDiff.java
new file mode 100644 (file)
index 0000000..a0ff471
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import javax.jcr.Value;
+
+/** The result of the comparison of two JCR properties. */
+public class PropertyDiff {
+       public final static Integer MODIFIED = 0;
+       public final static Integer ADDED = 1;
+       public final static Integer REMOVED = 2;
+
+       private final Integer type;
+       private final String relPath;
+       private final Value referenceValue;
+       private final Value newValue;
+
+       public PropertyDiff(Integer type, String relPath, Value referenceValue,
+                       Value newValue) {
+               super();
+
+               if (type == MODIFIED) {
+                       if (referenceValue == null || newValue == null)
+                               throw new ArgeoJcrException(
+                                               "Reference and new values must be specified.");
+               } else if (type == ADDED) {
+                       if (referenceValue != null || newValue == null)
+                               throw new ArgeoJcrException(
+                                               "New value and only it must be specified.");
+               } else if (type == REMOVED) {
+                       if (referenceValue == null || newValue != null)
+                               throw new ArgeoJcrException(
+                                               "Reference value and only it must be specified.");
+               } else {
+                       throw new ArgeoJcrException("Unkown diff type " + type);
+               }
+
+               if (relPath == null)
+                       throw new ArgeoJcrException("Relative path must be specified");
+
+               this.type = type;
+               this.relPath = relPath;
+               this.referenceValue = referenceValue;
+               this.newValue = newValue;
+       }
+
+       public Integer getType() {
+               return type;
+       }
+
+       public String getRelPath() {
+               return relPath;
+       }
+
+       public Value getReferenceValue() {
+               return referenceValue;
+       }
+
+       public Value getNewValue() {
+               return newValue;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/RepositoryRegister.java b/org.argeo.jcr/src/org/argeo/jcr/RepositoryRegister.java
new file mode 100644 (file)
index 0000000..2e3d455
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import java.util.Map;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryFactory;
+
+/** Allows to register repositories by name. */
+public interface RepositoryRegister extends RepositoryFactory {
+       /**
+        * The registered {@link Repository} as a read-only map. Note that this
+        * method should be called for each access in order to be sure to be up to
+        * date in case repositories have registered/unregistered
+        */
+       public Map<String, Repository> getRepositories();
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/ThreadBoundJcrSessionFactory.java b/org.argeo.jcr/src/org/argeo/jcr/ThreadBoundJcrSessionFactory.java
new file mode 100644 (file)
index 0000000..281bd01
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.LoginException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/** Proxy JCR sessions and attach them to calling threads. */
+@Deprecated
+public abstract class ThreadBoundJcrSessionFactory {
+       private final static Log log = LogFactory.getLog(ThreadBoundJcrSessionFactory.class);
+
+       private Repository repository;
+       /** can be injected as list, only used if repository is null */
+       private List<Repository> repositories;
+
+       private ThreadLocal<Session> session = new ThreadLocal<Session>();
+       private final Session proxiedSession;
+       /** If workspace is null, default will be used. */
+       private String workspace = null;
+
+       private String defaultUsername = "demo";
+       private String defaultPassword = "demo";
+       private Boolean forceDefaultCredentials = false;
+
+       private boolean active = true;
+
+       // monitoring
+       private final List<Thread> threads = Collections.synchronizedList(new ArrayList<Thread>());
+       private final Map<Long, Session> activeSessions = Collections.synchronizedMap(new HashMap<Long, Session>());
+       private MonitoringThread monitoringThread;
+
+       public ThreadBoundJcrSessionFactory() {
+               Class<?>[] interfaces = { Session.class };
+               proxiedSession = (Session) Proxy.newProxyInstance(ThreadBoundJcrSessionFactory.class.getClassLoader(),
+                               interfaces, new JcrSessionInvocationHandler());
+       }
+
+       /** Logs in to the repository using various strategies. */
+       protected synchronized Session login() {
+               if (!isActive())
+                       throw new ArgeoJcrException("Thread bound session factory inactive");
+
+               // discard session previously attached to this thread
+               Thread thread = Thread.currentThread();
+               if (activeSessions.containsKey(thread.getId())) {
+                       Session oldSession = activeSessions.remove(thread.getId());
+                       oldSession.logout();
+                       session.remove();
+               }
+
+               Session newSession = null;
+               // first try to login without credentials, assuming the underlying login
+               // module will have dealt with authentication (typically using Spring
+               // Security)
+               if (!forceDefaultCredentials)
+                       try {
+                               newSession = repository().login(workspace);
+                       } catch (LoginException e1) {
+                               log.warn("Cannot login without credentials: " + e1.getMessage());
+                               // invalid credentials, go to the next step
+                       } catch (RepositoryException e1) {
+                               // other kind of exception, fail
+                               throw new ArgeoJcrException("Cannot log in to repository", e1);
+                       }
+
+               // log using default username / password (useful for testing purposes)
+               if (newSession == null)
+                       try {
+                               SimpleCredentials sc = new SimpleCredentials(defaultUsername, defaultPassword.toCharArray());
+                               newSession = repository().login(sc, workspace);
+                       } catch (RepositoryException e) {
+                               throw new ArgeoJcrException("Cannot log in to repository", e);
+                       }
+
+               session.set(newSession);
+               // Log and monitor new session
+               if (log.isTraceEnabled())
+                       log.trace("Logged in to JCR session " + newSession + "; userId=" + newSession.getUserID());
+
+               // monitoring
+               activeSessions.put(thread.getId(), newSession);
+               threads.add(thread);
+               return newSession;
+       }
+
+       public Object getObject() {
+               return proxiedSession;
+       }
+
+       public void init() throws Exception {
+               // log.error("SHOULD NOT BE USED ANYMORE");
+               monitoringThread = new MonitoringThread();
+               monitoringThread.start();
+       }
+
+       public void dispose() throws Exception {
+               // if (activeSessions.size() == 0)
+               // return;
+
+               if (log.isTraceEnabled())
+                       log.trace("Cleaning up " + activeSessions.size() + " active JCR sessions...");
+
+               deactivate();
+               for (Session sess : activeSessions.values()) {
+                       JcrUtils.logoutQuietly(sess);
+               }
+               activeSessions.clear();
+       }
+
+       protected Boolean isActive() {
+               return active;
+       }
+
+       protected synchronized void deactivate() {
+               active = false;
+               notifyAll();
+       }
+
+       protected synchronized void removeSession(Thread thread) {
+               if (!isActive())
+                       return;
+               activeSessions.remove(thread.getId());
+               threads.remove(thread);
+       }
+
+       protected synchronized void cleanDeadThreads() {
+               if (!isActive())
+                       return;
+               Iterator<Thread> it = threads.iterator();
+               while (it.hasNext()) {
+                       Thread thread = it.next();
+                       if (!thread.isAlive() && isActive()) {
+                               if (activeSessions.containsKey(thread.getId())) {
+                                       Session session = activeSessions.get(thread.getId());
+                                       activeSessions.remove(thread.getId());
+                                       session.logout();
+                                       if (log.isTraceEnabled())
+                                               log.trace("Cleaned up JCR session (userID=" + session.getUserID() + ") from dead thread "
+                                                               + thread.getId());
+                               }
+                               it.remove();
+                       }
+               }
+               try {
+                       wait(1000);
+               } catch (InterruptedException e) {
+                       // silent
+               }
+       }
+
+       public Class<? extends Session> getObjectType() {
+               return Session.class;
+       }
+
+       public boolean isSingleton() {
+               return true;
+       }
+
+       /**
+        * Called before a method is actually called, allowing to check the session
+        * or re-login it (e.g. if authentication has changed). The default
+        * implementation returns the session.
+        */
+       protected Session preCall(Session session) {
+               return session;
+       }
+
+       protected Repository repository() {
+               if (repository != null)
+                       return repository;
+               if (repositories != null) {
+                       // hardened for OSGi dynamic services
+                       Iterator<Repository> it = repositories.iterator();
+                       if (it.hasNext())
+                               return it.next();
+               }
+               throw new ArgeoJcrException("No repository injected");
+       }
+
+       // /** Useful for declarative registration of OSGi services (blueprint) */
+       // public void register(Repository repository, Map<?, ?> params) {
+       // this.repository = repository;
+       // }
+       //
+       // /** Useful for declarative registration of OSGi services (blueprint) */
+       // public void unregister(Repository repository, Map<?, ?> params) {
+       // this.repository = null;
+       // }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setRepositories(List<Repository> repositories) {
+               this.repositories = repositories;
+       }
+
+       public void setDefaultUsername(String defaultUsername) {
+               this.defaultUsername = defaultUsername;
+       }
+
+       public void setDefaultPassword(String defaultPassword) {
+               this.defaultPassword = defaultPassword;
+       }
+
+       public void setForceDefaultCredentials(Boolean forceDefaultCredentials) {
+               this.forceDefaultCredentials = forceDefaultCredentials;
+       }
+
+       public void setWorkspace(String workspace) {
+               this.workspace = workspace;
+       }
+
+       protected class JcrSessionInvocationHandler implements InvocationHandler {
+
+               public Object invoke(Object proxy, Method method, Object[] args) throws Throwable, RepositoryException {
+                       Session threadSession = session.get();
+                       if (threadSession == null) {
+                               if ("logout".equals(method.getName()))// no need to login
+                                       return Void.TYPE;
+                               else if ("toString".equals(method.getName()))// maybe logging
+                                       return "Uninitialized Argeo thread bound JCR session";
+                               threadSession = login();
+                       }
+
+                       preCall(threadSession);
+                       Object ret;
+                       try {
+                               ret = method.invoke(threadSession, args);
+                       } catch (InvocationTargetException e) {
+                               Throwable cause = e.getCause();
+                               if (cause instanceof RepositoryException)
+                                       throw (RepositoryException) cause;
+                               else
+                                       throw cause;
+                       }
+                       if ("logout".equals(method.getName())) {
+                               session.remove();
+                               Thread thread = Thread.currentThread();
+                               removeSession(thread);
+                               if (log.isTraceEnabled())
+                                       log.trace("Logged out JCR session (userId=" + threadSession.getUserID() + ") on thread "
+                                                       + thread.getId());
+                       }
+                       return ret;
+               }
+       }
+
+       /** Monitors registered thread in order to clean up dead ones. */
+       private class MonitoringThread extends Thread {
+
+               public MonitoringThread() {
+                       super("ThreadBound JCR Session Monitor");
+               }
+
+               @Override
+               public void run() {
+                       while (isActive()) {
+                               cleanDeadThreads();
+                       }
+               }
+
+       }
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/UserJcrUtils.java b/org.argeo.jcr/src/org/argeo/jcr/UserJcrUtils.java
new file mode 100644 (file)
index 0000000..9526947
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.query.Query;
+import javax.jcr.query.qom.Constraint;
+import javax.jcr.query.qom.DynamicOperand;
+import javax.jcr.query.qom.QueryObjectModelFactory;
+import javax.jcr.query.qom.Selector;
+import javax.jcr.query.qom.StaticOperand;
+
+/**
+ * Utilities related to the user home and properties based on Argeo JCR model.
+ * Do not use anymore. Does not fit with current security model
+ */
+@Deprecated
+public class UserJcrUtils {
+       /** The home base path. Not yet configurable */
+       public final static String DEFAULT_HOME_BASE_PATH = "/home";
+
+       /**
+        * Returns the home node of the user or null if none was found.
+        * 
+        * @param session
+        *            the session to use in order to perform the search, this can be
+        *            a session with a different user ID than the one searched,
+        *            typically when a system or admin session is used.
+        * @param username
+        *            the username of the user
+        */
+       public static Node getUserHome(Session session, String username) {
+               try {
+                       // String homePath = UserJcrUtils.getUserHomePath(username);
+                       // return session.itemExists(homePath) ? session.getNode(homePath)
+                       // : null;
+                       // kept for example of QOM queries
+                       QueryObjectModelFactory qomf = session.getWorkspace()
+                                       .getQueryManager().getQOMFactory();
+                       Selector userHomeSel = qomf.selector(ArgeoTypes.ARGEO_USER_HOME,
+                                       "userHome");
+                       DynamicOperand userIdDop = qomf.propertyValue(
+                                       userHomeSel.getSelectorName(), ArgeoNames.ARGEO_USER_ID);
+                       StaticOperand userIdSop = qomf.literal(session.getValueFactory()
+                                       .createValue(username));
+                       Constraint constraint = qomf.comparison(userIdDop,
+                                       QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop);
+                       Query query = qomf.createQuery(userHomeSel, constraint, null, null);
+                       return JcrUtils.querySingleNode(query);
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot find home for user " + username, e);
+               }
+       }
+
+       public static Node getUserProfile(Session session, String username) {
+               try {
+                       QueryObjectModelFactory qomf = session.getWorkspace()
+                                       .getQueryManager().getQOMFactory();
+                       Selector userHomeSel = qomf.selector(ArgeoTypes.ARGEO_USER_PROFILE,
+                                       "userProfile");
+                       DynamicOperand userIdDop = qomf.propertyValue(
+                                       userHomeSel.getSelectorName(), ArgeoNames.ARGEO_USER_ID);
+                       StaticOperand userIdSop = qomf.literal(session.getValueFactory()
+                                       .createValue(username));
+                       Constraint constraint = qomf.comparison(userIdDop,
+                                       QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop);
+                       Query query = qomf.createQuery(userHomeSel, constraint, null, null);
+                       return JcrUtils.querySingleNode(query);
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException(
+                                       "Cannot find profile for user " + username, e);
+               }
+       }
+
+       /** Returns the home node of the session user or null if none was found. */
+       public static Node getUserHome(Session session) {
+               String userID = session.getUserID();
+               return getUserHome(session, userID);
+       }
+
+       private UserJcrUtils() {
+       }
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/VersionDiff.java b/org.argeo.jcr/src/org/argeo/jcr/VersionDiff.java
new file mode 100644 (file)
index 0000000..e6ae913
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr;
+
+import java.util.Calendar;
+import java.util.Map;
+
+/**
+ * Generic Object that enables the creation of history reports based on a JCR
+ * versionable node. userId and creation date are added to the map of
+ * PropertyDiff.
+ * 
+ * These two fields might be null
+ * 
+ */
+public class VersionDiff {
+
+       private String userId;
+       private Map<String, PropertyDiff> diffs;
+       private Calendar updateTime;
+
+       public VersionDiff(String userId, Calendar updateTime,
+                       Map<String, PropertyDiff> diffs) {
+               this.userId = userId;
+               this.updateTime = updateTime;
+               this.diffs = diffs;
+       }
+
+       public String getUserId() {
+               return userId;
+       }
+
+       public Map<String, PropertyDiff> getDiffs() {
+               return diffs;
+       }
+
+       public Calendar getUpdateTime() {
+               return updateTime;
+       }
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/argeo.cnd b/org.argeo.jcr/src/org/argeo/jcr/argeo.cnd
new file mode 100644 (file)
index 0000000..fbfea9d
--- /dev/null
@@ -0,0 +1,72 @@
+<argeo = 'http://www.argeo.org/ns/argeo'>
+
+// GENERIC TYPES NOT AVAILABLE IN JCR
+[argeo:link] > mix:created, mix:lastModified
+mixin
+// URI(s)
+- argeo:uri (STRING) m
+
+[argeo:references] > nt:unstructured
+- * (REFERENCE) *
+
+// DATA MODEL
+[argeo:dataModel] > mix:created, mix:lastModified, mix:versionable
+mixin
+- argeo:uri (STRING) m
+- argeo:dataModelVersion (STRING) m
+
+// USER NODES
+// user should be lower case, between 3 and 15 characters long
+[argeo:userHome] > mix:created, mix:lastModified
+mixin
+- argeo:userID (STRING) m
+- argeo:remoteRoles (STRING) *
+// deprecated. for backward compatibility:
++ argeo:profile (argeo:userProfile)
++ argeo:keyring (argeo:pbeSpec)
++ argeo:preferences (argeo:preferenceNode)
+
+[argeo:userProfile] > mix:created, mix:lastModified, mix:title, mix:versionable
+mixin
+- argeo:userID (STRING) m
+- argeo:enabled (BOOLEAN)
+- argeo:accountNonExpired (BOOLEAN)
+- argeo:accountNonLocked (BOOLEAN)
+- argeo:credentialsNonExpired (BOOLEAN)
+
+[argeo:preferenceNode] > mix:lastModified, mix:versionable
+mixin
++ * (argeo:preferenceNode) * version
+
+[argeo:remoteRepository] > nt:unstructured
+- argeo:uri (STRING)
+- argeo:userID (STRING)
++ argeo:password (argeo:encrypted)
+
+// TABULAR CONTENT
+[argeo:table] > nt:file
++ * (argeo:column) *
+
+[argeo:column] > mix:title
+- jcr:requiredType (STRING) = 'STRING'
+
+[argeo:csv] > nt:resource
+
+// CRYPTO
+[argeo:encrypted] > nt:base
+mixin
+// initialization vector used by some algorithms
+- argeo:iv (BINARY)
+
+[argeo:pbeKeySpec] > nt:base
+mixin
+- argeo:secretKeyFactory (STRING)
+- argeo:salt (BINARY)
+- argeo:iterationCount (LONG)
+- argeo:keyLength (LONG)
+- argeo:secretKeyEncryption (STRING)
+
+[argeo:pbeSpec] > argeo:pbeKeySpec
+mixin
+- argeo:cipher (STRING)
+
diff --git a/org.argeo.jcr/src/org/argeo/jcr/docbook/DocBookModel.java b/org.argeo.jcr/src/org/argeo/jcr/docbook/DocBookModel.java
new file mode 100644 (file)
index 0000000..61a902d
--- /dev/null
@@ -0,0 +1,42 @@
+package org.argeo.jcr.docbook;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class DocBookModel {
+       private final static Log log = LogFactory.getLog(DocBookModel.class);
+       private Session session;
+
+       public DocBookModel(Session session) {
+               super();
+               this.session = session;
+       }
+
+       public void setSession(Session session) {
+               this.session = session;
+       }
+
+       public void importXml(String path, InputStream in)
+                       throws RepositoryException, IOException {
+               long begin = System.currentTimeMillis();
+               session.importXML(path, in,
+                               ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
+               long duration = System.currentTimeMillis() - begin;
+               if (log.isTraceEnabled())
+                       log.trace("Imported " + path + " in " + duration + " ms");
+
+       }
+
+       public void exportXml(String path, OutputStream out)
+                       throws RepositoryException, IOException {
+               session.exportDocumentView(path, out, true, false);
+       }
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/docbook/DocBookNames.java b/org.argeo.jcr/src/org/argeo/jcr/docbook/DocBookNames.java
new file mode 100644 (file)
index 0000000..f4edb12
--- /dev/null
@@ -0,0 +1,7 @@
+package org.argeo.jcr.docbook;
+
+public interface DocBookNames {
+       public final static String DBK_ = "dbk:";
+       public final static String DBK_PARA = DBK_ + "para";
+       public final static String DBK_SECTION = DBK_ + "section";
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/docbook/docbook-full.cnd b/org.argeo.jcr/src/org/argeo/jcr/docbook/docbook-full.cnd
new file mode 100644 (file)
index 0000000..7e4fefa
--- /dev/null
@@ -0,0 +1,3005 @@
+<dbk = 'http://docbook.org/ns/docbook'>
+<argeodbk = 'http://www.argeo.org/ns/argeodbk'>
+<jcr = 'http://www.jcp.org/jcr/1.0'>
+<nt = 'http://www.jcp.org/jcr/nt/1.0'>
+<xlink = 'http://www.w3.org/1999/xlink'>
+<xs = 'http://www.w3.org/2001/XMLSchema'>
+<xml = 'http://www.w3.org/XML/1998/namespace'>
+
+[argeodbk:titled]
+mixin
+ + dbk:info (dbk:info) = dbk:info *
+ + dbk:title (dbk:title) = dbk:title *
+ + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev *
+
+[argeodbk:linkingAttributes]
+mixin
+ - dbk:linkend (String)
+ - xlink:actuate (String)
+ - xlink:arcrole (String)
+ - xlink:href (String)
+ - xlink:role (String)
+ - xlink:show (String)
+ - xlink:title (String)
+ - xlink:type (String)
+
+[argeodbk:freeText]
+mixin
+ + dbk:phrase (dbk:phrase) = dbk:phrase *
+ + dbk:replaceable (dbk:replaceable) = dbk:replaceable *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[argeodbk:markupInlines]
+mixin
+ + dbk:code (dbk:code) = dbk:code *
+ + dbk:constant (dbk:constant) = dbk:constant *
+ + dbk:email (dbk:email) = dbk:email *
+ + dbk:literal (dbk:literal) = dbk:literal *
+ + dbk:markup (dbk:markup) = dbk:markup *
+ + dbk:symbol (dbk:symbol) = dbk:symbol *
+ + dbk:tag (dbk:tag) = dbk:tag *
+ + dbk:token (dbk:token) = dbk:token *
+ + dbk:uri (dbk:uri) = dbk:uri *
+
+[argeodbk:listElements]
+mixin
+ + dbk:bibliolist (dbk:bibliolist) = dbk:bibliolist *
+ + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist *
+ + dbk:glosslist (dbk:glosslist) = dbk:glosslist *
+ + dbk:itemizedlist (dbk:itemizedlist) = dbk:itemizedlist *
+ + dbk:orderedlist (dbk:orderedlist) = dbk:orderedlist *
+ + dbk:procedure (dbk:procedure) = dbk:procedure *
+ + dbk:qandaset (dbk:qandaset) = dbk:qandaset *
+ + dbk:segmentedlist (dbk:segmentedlist) = dbk:segmentedlist *
+ + dbk:simplelist (dbk:simplelist) = dbk:simplelist *
+ + dbk:variablelist (dbk:variablelist) = dbk:variablelist *
+
+[argeodbk:paragraphElements]
+mixin
+ + dbk:formalpara (dbk:formalpara) = dbk:formalpara *
+ + dbk:para (dbk:para) = dbk:para *
+ + dbk:simpara (dbk:simpara) = dbk:simpara *
+
+[argeodbk:indexingInlines]
+mixin
+ + dbk:indexterm (dbk:indexterm) = dbk:indexterm *
+
+[argeodbk:techDocElements]
+mixin
+ + dbk:caution (dbk:caution) = dbk:caution *
+ + dbk:classsynopsis (dbk:classsynopsis) = dbk:classsynopsis *
+ + dbk:cmdsynopsis (dbk:cmdsynopsis) = dbk:cmdsynopsis *
+ + dbk:constraintdef (dbk:constraintdef) = dbk:constraintdef *
+ + dbk:constructorsynopsis (dbk:constructorsynopsis) = dbk:constructorsynopsis *
+ + dbk:destructorsynopsis (dbk:destructorsynopsis) = dbk:destructorsynopsis *
+ + dbk:equation (dbk:equation) = dbk:equation *
+ + dbk:example (dbk:example) = dbk:example *
+ + dbk:fieldsynopsis (dbk:fieldsynopsis) = dbk:fieldsynopsis *
+ + dbk:figure (dbk:figure) = dbk:figure *
+ + dbk:funcsynopsis (dbk:funcsynopsis) = dbk:funcsynopsis *
+ + dbk:important (dbk:important) = dbk:important *
+ + dbk:informalequation (dbk:informalequation) = dbk:informalequation *
+ + dbk:informalexample (dbk:informalexample) = dbk:informalexample *
+ + dbk:informalfigure (dbk:informalfigure) = dbk:informalfigure *
+ + dbk:informaltable (dbk:informaltable) = dbk:informaltable *
+ + dbk:literallayout (dbk:literallayout) = dbk:literallayout *
+ + dbk:methodsynopsis (dbk:methodsynopsis) = dbk:methodsynopsis *
+ + dbk:msgset (dbk:msgset) = dbk:msgset *
+ + dbk:note (dbk:note) = dbk:note *
+ + dbk:productionset (dbk:productionset) = dbk:productionset *
+ + dbk:programlisting (dbk:programlisting) = dbk:programlisting *
+ + dbk:programlistingco (dbk:programlistingco) = dbk:programlistingco *
+ + dbk:screen (dbk:screen) = dbk:screen *
+ + dbk:screenco (dbk:screenco) = dbk:screenco *
+ + dbk:synopsis (dbk:synopsis) = dbk:synopsis *
+ + dbk:table (dbk:table) = dbk:table *
+ + dbk:task (dbk:task) = dbk:task *
+ + dbk:tip (dbk:tip) = dbk:tip *
+ + dbk:warning (dbk:warning) = dbk:warning *
+
+[argeodbk:techDocInlines]
+mixin
+ + dbk:accel (dbk:accel) = dbk:accel *
+ + dbk:application (dbk:application) = dbk:application *
+ + dbk:classname (dbk:classname) = dbk:classname *
+ + dbk:command (dbk:command) = dbk:command *
+ + dbk:computeroutput (dbk:computeroutput) = dbk:computeroutput *
+ + dbk:database (dbk:database) = dbk:database *
+ + dbk:envar (dbk:envar) = dbk:envar *
+ + dbk:errorcode (dbk:errorcode) = dbk:errorcode *
+ + dbk:errorname (dbk:errorname) = dbk:errorname *
+ + dbk:errortext (dbk:errortext) = dbk:errortext *
+ + dbk:errortype (dbk:errortype) = dbk:errortype *
+ + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname *
+ + dbk:filename (dbk:filename) = dbk:filename *
+ + dbk:function (dbk:function) = dbk:function *
+ + dbk:guibutton (dbk:guibutton) = dbk:guibutton *
+ + dbk:guiicon (dbk:guiicon) = dbk:guiicon *
+ + dbk:guilabel (dbk:guilabel) = dbk:guilabel *
+ + dbk:guimenu (dbk:guimenu) = dbk:guimenu *
+ + dbk:guimenuitem (dbk:guimenuitem) = dbk:guimenuitem *
+ + dbk:guisubmenu (dbk:guisubmenu) = dbk:guisubmenu *
+ + dbk:hardware (dbk:hardware) = dbk:hardware *
+ + dbk:initializer (dbk:initializer) = dbk:initializer *
+ + dbk:inlineequation (dbk:inlineequation) = dbk:inlineequation *
+ + dbk:interfacename (dbk:interfacename) = dbk:interfacename *
+ + dbk:keycap (dbk:keycap) = dbk:keycap *
+ + dbk:keycode (dbk:keycode) = dbk:keycode *
+ + dbk:keycombo (dbk:keycombo) = dbk:keycombo *
+ + dbk:keysym (dbk:keysym) = dbk:keysym *
+ + dbk:menuchoice (dbk:menuchoice) = dbk:menuchoice *
+ + dbk:methodname (dbk:methodname) = dbk:methodname *
+ + dbk:modifier (dbk:modifier) = dbk:modifier *
+ + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton *
+ + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal *
+ + dbk:ooclass (dbk:ooclass) = dbk:ooclass *
+ + dbk:ooexception (dbk:ooexception) = dbk:ooexception *
+ + dbk:oointerface (dbk:oointerface) = dbk:oointerface *
+ + dbk:option (dbk:option) = dbk:option *
+ + dbk:optional (dbk:optional) = dbk:optional *
+ + dbk:package (dbk:package) = dbk:package *
+ + dbk:parameter (dbk:parameter) = dbk:parameter *
+ + dbk:productname (dbk:productname) = dbk:productname *
+ + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
+ + dbk:prompt (dbk:prompt) = dbk:prompt *
+ + dbk:property (dbk:property) = dbk:property *
+ + dbk:returnvalue (dbk:returnvalue) = dbk:returnvalue *
+ + dbk:shortcut (dbk:shortcut) = dbk:shortcut *
+ + dbk:systemitem (dbk:systemitem) = dbk:systemitem *
+ + dbk:termdef (dbk:termdef) = dbk:termdef *
+ + dbk:trademark (dbk:trademark) = dbk:trademark *
+ + dbk:type (dbk:type) = dbk:type *
+ + dbk:userinput (dbk:userinput) = dbk:userinput *
+ + dbk:varname (dbk:varname) = dbk:varname *
+
+[argeodbk:publishingElements]
+mixin
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:blockquote (dbk:blockquote) = dbk:blockquote *
+ + dbk:epigraph (dbk:epigraph) = dbk:epigraph *
+ + dbk:sidebar (dbk:sidebar) = dbk:sidebar *
+
+[argeodbk:ubiquitousInlines]
+mixin
+ + dbk:alt (dbk:alt) = dbk:alt *
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:biblioref (dbk:biblioref) = dbk:biblioref *
+ + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
+ + dbk:link (dbk:link) = dbk:link *
+ + dbk:olink (dbk:olink) = dbk:olink *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:subscript (dbk:subscript) = dbk:subscript *
+ + dbk:superscript (dbk:superscript) = dbk:superscript *
+ + dbk:xref (dbk:xref) = dbk:xref *
+
+[argeodbk:abstractSection]
+mixin
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bibliography (dbk:bibliography) = dbk:bibliography *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:glossary (dbk:glossary) = dbk:glossary *
+ + dbk:index (dbk:index) = dbk:index *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ + dbk:toc (dbk:toc) = dbk:toc *
+ - dbk:label (String)
+ - dbk:status (String)
+
+[argeodbk:bibliographyInlines]
+mixin
+ + dbk:author (dbk:author) = dbk:author *
+ + dbk:citation (dbk:citation) = dbk:citation *
+ + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid *
+ + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry *
+ + dbk:citetitle (dbk:citetitle) = dbk:citetitle *
+ + dbk:editor (dbk:editor) = dbk:editor *
+ + dbk:jobtitle (dbk:jobtitle) = dbk:jobtitle *
+ + dbk:org (dbk:org) = dbk:org *
+ + dbk:orgname (dbk:orgname) = dbk:orgname *
+ + dbk:person (dbk:person) = dbk:person *
+ + dbk:personname (dbk:personname) = dbk:personname *
+
+[argeodbk:publishingInlines]
+mixin
+ + dbk:abbrev (dbk:abbrev) = dbk:abbrev *
+ + dbk:acronym (dbk:acronym) = dbk:acronym *
+ + dbk:coref (dbk:coref) = dbk:coref *
+ + dbk:date (dbk:date) = dbk:date *
+ + dbk:emphasis (dbk:emphasis) = dbk:emphasis *
+ + dbk:firstterm (dbk:firstterm) = dbk:firstterm *
+ + dbk:footnote (dbk:footnote) = dbk:footnote *
+ + dbk:footnoteref (dbk:footnoteref) = dbk:footnoteref *
+ + dbk:foreignphrase (dbk:foreignphrase) = dbk:foreignphrase *
+ + dbk:glossterm (dbk:glossterm) = dbk:glossterm *
+ + dbk:quote (dbk:quote) = dbk:quote *
+ + dbk:wordasword (dbk:wordasword) = dbk:wordasword *
+
+[argeodbk:base]
+abstract
+ - dbk:annotations (String)
+ - dbk:arch (String)
+ - dbk:audience (String)
+ - dbk:condition (String)
+ - dbk:conformance (String)
+ - dbk:dir (String)
+ - dbk:os (String)
+ - dbk:remap (String)
+ - dbk:revision (String)
+ - dbk:revisionflag (String)
+ - dbk:role (String)
+ - dbk:security (String)
+ - dbk:userlevel (String)
+ - dbk:vendor (String)
+ - dbk:version (String)
+ - dbk:wordsize (String)
+ - dbk:xreflabel (String)
+ - xml:base (String)
+ - xml:id (String)
+ - xml:lang (String)
+
+[dbk:abbrev] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:trademark (dbk:trademark) = dbk:trademark *
+
+[dbk:abstract] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+
+[dbk:accel] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:acknowledgements] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:acronym] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:trademark (dbk:trademark) = dbk:trademark *
+
+[dbk:address] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:city (dbk:city) = dbk:city *
+ + dbk:country (dbk:country) = dbk:country *
+ + dbk:email (dbk:email) = dbk:email *
+ + dbk:fax (dbk:fax) = dbk:fax *
+ + dbk:otheraddr (dbk:otheraddr) = dbk:otheraddr *
+ + dbk:personname (dbk:personname) = dbk:personname *
+ + dbk:phone (dbk:phone) = dbk:phone *
+ + dbk:pob (dbk:pob) = dbk:pob *
+ + dbk:postcode (dbk:postcode) = dbk:postcode *
+ + dbk:state (dbk:state) = dbk:state *
+ + dbk:street (dbk:street) = dbk:street *
+ + dbk:uri (dbk:uri) = dbk:uri *
+ - dbk:continuation (String) 
+ - dbk:language (String) 
+ - dbk:linenumbering (String) 
+ - dbk:startinglinenumber (String) 
+ - xml:space (String) 
+
+[dbk:affiliation] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:jobtitle (dbk:jobtitle) = dbk:jobtitle *
+ + dbk:org (dbk:org) = dbk:org
+ + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
+ + dbk:orgname (dbk:orgname) = dbk:orgname
+ + dbk:shortaffil (dbk:shortaffil) = dbk:shortaffil
+
+[dbk:alt] > argeodbk:base
+ + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:anchor] > argeodbk:base
+
+[dbk:annotation] > argeodbk:base, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:annotates (String) 
+
+[dbk:answer] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:label (dbk:label) = dbk:label
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:appendix] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:refentry (dbk:refentry) = dbk:refentry *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:sect1 (dbk:sect1) = dbk:sect1 *
+ + dbk:section (dbk:section) = dbk:section *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+
+[dbk:application] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+
+[dbk:arc] > argeodbk:base
+ - xlink:from (String) 
+ - xlink:to (String) 
+
+[dbk:area] > argeodbk:base
+ + dbk:alt (dbk:alt) = dbk:alt
+ - dbk:coords (String) 
+ - dbk:label (String) 
+ - dbk:linkends (String) 
+ - dbk:otherunits (String) 
+ - dbk:units (String) 
+
+[dbk:areaset] > argeodbk:base
+ + dbk:area (dbk:area) = dbk:area *
+ - dbk:label (String) 
+ - dbk:linkends (String) 
+ - dbk:otherunits (String) 
+ - dbk:units (String) 
+
+[dbk:areaspec] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:area (dbk:area) = dbk:area *
+ + dbk:areaset (dbk:areaset) = dbk:areaset *
+ - dbk:otherunits (String) 
+ - dbk:units (String) 
+
+[dbk:arg] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:arg (dbk:arg) = dbk:arg *
+ + dbk:group (dbk:group) = dbk:group *
+ + dbk:option (dbk:option) = dbk:option *
+ + dbk:sbr (dbk:sbr) = dbk:sbr *
+ + dbk:synopfragmentref (dbk:synopfragmentref) = dbk:synopfragmentref *
+ - dbk:choice (String) 
+ - dbk:rep (String) 
+
+[dbk:article] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:acknowledgements (dbk:acknowledgements) = dbk:acknowledgements *
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:appendix (dbk:appendix) = dbk:appendix *
+ + dbk:colophon (dbk:colophon) = dbk:colophon *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:refentry (dbk:refentry) = dbk:refentry *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:sect1 (dbk:sect1) = dbk:sect1 *
+ + dbk:section (dbk:section) = dbk:section *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+ - dbk:class (String) 
+
+[dbk:artpagenums] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:attribution] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:citation (dbk:citation) = dbk:citation *
+ + dbk:citetitle (dbk:citetitle) = dbk:citetitle *
+ + dbk:person (dbk:person) = dbk:person *
+ + dbk:personname (dbk:personname) = dbk:personname *
+
+[dbk:audiodata] > argeodbk:base
+ + dbk:info (dbk:info) = dbk:info
+ - dbk:entityref (String) 
+ - dbk:fileref (String) 
+ - dbk:format (String) 
+
+[dbk:audioobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:audiodata (dbk:audiodata) = dbk:audiodata
+ + dbk:info (dbk:info) = dbk:info
+
+[dbk:author] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:affiliation (dbk:affiliation) = dbk:affiliation *
+ + dbk:contrib (dbk:contrib) = dbk:contrib *
+ + dbk:email (dbk:email) = dbk:email *
+ + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
+ + dbk:orgname (dbk:orgname) = dbk:orgname
+ + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
+ + dbk:personname (dbk:personname) = dbk:personname
+ + dbk:uri (dbk:uri) = dbk:uri *
+
+[dbk:authorgroup] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:author (dbk:author) = dbk:author *
+ + dbk:editor (dbk:editor) = dbk:editor *
+ + dbk:othercredit (dbk:othercredit) = dbk:othercredit *
+
+[dbk:authorinitials] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:bibliocoverage] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:otherspatial (String) 
+ - dbk:othertemporal (String) 
+ - dbk:spatial (String) 
+ - dbk:temporal (String) 
+
+[dbk:bibliodiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:biblioentry (dbk:biblioentry) = dbk:biblioentry *
+ + dbk:bibliomixed (dbk:bibliomixed) = dbk:bibliomixed *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:biblioentry] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:publishingInlines
+ + dbk:abstract (dbk:abstract) = dbk:abstract *
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums *
+ + dbk:author (dbk:author) = dbk:author *
+ + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup *
+ + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials *
+ + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage *
+ + dbk:biblioid (dbk:biblioid) = dbk:biblioid *
+ + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc *
+ + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset *
+ + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation *
+ + dbk:biblioset (dbk:biblioset) = dbk:biblioset *
+ + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource *
+ + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid *
+ + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry *
+ + dbk:citetitle (dbk:citetitle) = dbk:citetitle *
+ + dbk:collab (dbk:collab) = dbk:collab *
+ + dbk:confgroup (dbk:confgroup) = dbk:confgroup *
+ + dbk:contractnum (dbk:contractnum) = dbk:contractnum *
+ + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor *
+ + dbk:copyright (dbk:copyright) = dbk:copyright *
+ + dbk:cover (dbk:cover) = dbk:cover *
+ + dbk:edition (dbk:edition) = dbk:edition *
+ + dbk:editor (dbk:editor) = dbk:editor *
+ + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink *
+ + dbk:issuenum (dbk:issuenum) = dbk:issuenum *
+ + dbk:itermset (dbk:itermset) = dbk:itermset *
+ + dbk:keywordset (dbk:keywordset) = dbk:keywordset *
+ + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:org (dbk:org) = dbk:org *
+ + dbk:orgname (dbk:orgname) = dbk:orgname *
+ + dbk:othercredit (dbk:othercredit) = dbk:othercredit *
+ + dbk:pagenums (dbk:pagenums) = dbk:pagenums *
+ + dbk:person (dbk:person) = dbk:person *
+ + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
+ + dbk:personname (dbk:personname) = dbk:personname *
+ + dbk:phrase (dbk:phrase) = dbk:phrase *
+ + dbk:printhistory (dbk:printhistory) = dbk:printhistory *
+ + dbk:productname (dbk:productname) = dbk:productname *
+ + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
+ + dbk:pubdate (dbk:pubdate) = dbk:pubdate *
+ + dbk:publisher (dbk:publisher) = dbk:publisher *
+ + dbk:publishername (dbk:publishername) = dbk:publishername *
+ + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums *
+ + dbk:subjectset (dbk:subjectset) = dbk:subjectset *
+ + dbk:subscript (dbk:subscript) = dbk:subscript *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ + dbk:superscript (dbk:superscript) = dbk:superscript *
+ + dbk:title (dbk:title) = dbk:title *
+ + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev *
+ + dbk:volumenum (dbk:volumenum) = dbk:volumenum *
+
+[dbk:bibliography] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bibliodiv (dbk:bibliodiv) = dbk:bibliodiv *
+ + dbk:biblioentry (dbk:biblioentry) = dbk:biblioentry *
+ + dbk:bibliomixed (dbk:bibliomixed) = dbk:bibliomixed *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:biblioid] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+ - dbk:otherclass (String) 
+
+[dbk:bibliolist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:biblioentry (dbk:biblioentry) = dbk:biblioentry *
+ + dbk:bibliomixed (dbk:bibliomixed) = dbk:bibliomixed *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:bibliomisc] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:bibliomixed] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:publishingInlines
+ + dbk:abstract (dbk:abstract) = dbk:abstract *
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums *
+ + dbk:author (dbk:author) = dbk:author *
+ + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup *
+ + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials *
+ + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage *
+ + dbk:biblioid (dbk:biblioid) = dbk:biblioid *
+ + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc *
+ + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset *
+ + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation *
+ + dbk:biblioset (dbk:biblioset) = dbk:biblioset *
+ + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource *
+ + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid *
+ + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry *
+ + dbk:citetitle (dbk:citetitle) = dbk:citetitle *
+ + dbk:collab (dbk:collab) = dbk:collab *
+ + dbk:confgroup (dbk:confgroup) = dbk:confgroup *
+ + dbk:contractnum (dbk:contractnum) = dbk:contractnum *
+ + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor *
+ + dbk:copyright (dbk:copyright) = dbk:copyright *
+ + dbk:cover (dbk:cover) = dbk:cover *
+ + dbk:edition (dbk:edition) = dbk:edition *
+ + dbk:editor (dbk:editor) = dbk:editor *
+ + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink *
+ + dbk:issuenum (dbk:issuenum) = dbk:issuenum *
+ + dbk:itermset (dbk:itermset) = dbk:itermset *
+ + dbk:keywordset (dbk:keywordset) = dbk:keywordset *
+ + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:org (dbk:org) = dbk:org *
+ + dbk:orgname (dbk:orgname) = dbk:orgname *
+ + dbk:othercredit (dbk:othercredit) = dbk:othercredit *
+ + dbk:pagenums (dbk:pagenums) = dbk:pagenums *
+ + dbk:person (dbk:person) = dbk:person *
+ + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
+ + dbk:personname (dbk:personname) = dbk:personname *
+ + dbk:phrase (dbk:phrase) = dbk:phrase *
+ + dbk:printhistory (dbk:printhistory) = dbk:printhistory *
+ + dbk:productname (dbk:productname) = dbk:productname *
+ + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
+ + dbk:pubdate (dbk:pubdate) = dbk:pubdate *
+ + dbk:publisher (dbk:publisher) = dbk:publisher *
+ + dbk:publishername (dbk:publishername) = dbk:publishername *
+ + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums *
+ + dbk:subjectset (dbk:subjectset) = dbk:subjectset *
+ + dbk:subscript (dbk:subscript) = dbk:subscript *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ + dbk:superscript (dbk:superscript) = dbk:superscript *
+ + dbk:title (dbk:title) = dbk:title *
+ + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev *
+ + dbk:volumenum (dbk:volumenum) = dbk:volumenum *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:bibliomset] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:publishingInlines, argeodbk:ubiquitousInlines
+ + dbk:abstract (dbk:abstract) = dbk:abstract *
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums *
+ + dbk:author (dbk:author) = dbk:author *
+ + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup *
+ + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials *
+ + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage *
+ + dbk:biblioid (dbk:biblioid) = dbk:biblioid *
+ + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc *
+ + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset *
+ + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation *
+ + dbk:biblioset (dbk:biblioset) = dbk:biblioset *
+ + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource *
+ + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid *
+ + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry *
+ + dbk:citetitle (dbk:citetitle) = dbk:citetitle *
+ + dbk:collab (dbk:collab) = dbk:collab *
+ + dbk:confgroup (dbk:confgroup) = dbk:confgroup *
+ + dbk:contractnum (dbk:contractnum) = dbk:contractnum *
+ + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor *
+ + dbk:copyright (dbk:copyright) = dbk:copyright *
+ + dbk:cover (dbk:cover) = dbk:cover *
+ + dbk:edition (dbk:edition) = dbk:edition *
+ + dbk:editor (dbk:editor) = dbk:editor *
+ + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink *
+ + dbk:issuenum (dbk:issuenum) = dbk:issuenum *
+ + dbk:itermset (dbk:itermset) = dbk:itermset *
+ + dbk:keywordset (dbk:keywordset) = dbk:keywordset *
+ + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:org (dbk:org) = dbk:org *
+ + dbk:orgname (dbk:orgname) = dbk:orgname *
+ + dbk:othercredit (dbk:othercredit) = dbk:othercredit *
+ + dbk:pagenums (dbk:pagenums) = dbk:pagenums *
+ + dbk:person (dbk:person) = dbk:person *
+ + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
+ + dbk:personname (dbk:personname) = dbk:personname *
+ + dbk:printhistory (dbk:printhistory) = dbk:printhistory *
+ + dbk:productname (dbk:productname) = dbk:productname *
+ + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
+ + dbk:pubdate (dbk:pubdate) = dbk:pubdate *
+ + dbk:publisher (dbk:publisher) = dbk:publisher *
+ + dbk:publishername (dbk:publishername) = dbk:publishername *
+ + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums *
+ + dbk:subjectset (dbk:subjectset) = dbk:subjectset *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ + dbk:title (dbk:title) = dbk:title *
+ + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev *
+ + dbk:volumenum (dbk:volumenum) = dbk:volumenum *
+ - dbk:relation (String) 
+
+[dbk:biblioref] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:begin (String) 
+ - dbk:end (String) 
+ - dbk:endterm (Reference) 
+ - dbk:units (String) 
+ - dbk:xrefstyle (String) 
+
+[dbk:bibliorelation] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+ - dbk:otherclass (String) 
+ - dbk:othertype (String) 
+ - dbk:type (String) 
+
+[dbk:biblioset] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:publishingInlines
+ + dbk:abstract (dbk:abstract) = dbk:abstract *
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums *
+ + dbk:author (dbk:author) = dbk:author *
+ + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup *
+ + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials *
+ + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage *
+ + dbk:biblioid (dbk:biblioid) = dbk:biblioid *
+ + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc *
+ + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset *
+ + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation *
+ + dbk:biblioset (dbk:biblioset) = dbk:biblioset *
+ + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource *
+ + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid *
+ + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry *
+ + dbk:citetitle (dbk:citetitle) = dbk:citetitle *
+ + dbk:collab (dbk:collab) = dbk:collab *
+ + dbk:confgroup (dbk:confgroup) = dbk:confgroup *
+ + dbk:contractnum (dbk:contractnum) = dbk:contractnum *
+ + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor *
+ + dbk:copyright (dbk:copyright) = dbk:copyright *
+ + dbk:cover (dbk:cover) = dbk:cover *
+ + dbk:edition (dbk:edition) = dbk:edition *
+ + dbk:editor (dbk:editor) = dbk:editor *
+ + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink *
+ + dbk:issuenum (dbk:issuenum) = dbk:issuenum *
+ + dbk:itermset (dbk:itermset) = dbk:itermset *
+ + dbk:keywordset (dbk:keywordset) = dbk:keywordset *
+ + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:org (dbk:org) = dbk:org *
+ + dbk:orgname (dbk:orgname) = dbk:orgname *
+ + dbk:othercredit (dbk:othercredit) = dbk:othercredit *
+ + dbk:pagenums (dbk:pagenums) = dbk:pagenums *
+ + dbk:person (dbk:person) = dbk:person *
+ + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
+ + dbk:personname (dbk:personname) = dbk:personname *
+ + dbk:phrase (dbk:phrase) = dbk:phrase *
+ + dbk:printhistory (dbk:printhistory) = dbk:printhistory *
+ + dbk:productname (dbk:productname) = dbk:productname *
+ + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
+ + dbk:pubdate (dbk:pubdate) = dbk:pubdate *
+ + dbk:publisher (dbk:publisher) = dbk:publisher *
+ + dbk:publishername (dbk:publishername) = dbk:publishername *
+ + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums *
+ + dbk:subjectset (dbk:subjectset) = dbk:subjectset *
+ + dbk:subscript (dbk:subscript) = dbk:subscript *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ + dbk:superscript (dbk:superscript) = dbk:superscript *
+ + dbk:title (dbk:title) = dbk:title *
+ + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev *
+ + dbk:volumenum (dbk:volumenum) = dbk:volumenum *
+ - dbk:relation (String) 
+
+[dbk:bibliosource] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+ - dbk:otherclass (String) 
+
+[dbk:blockquote] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:attribution (dbk:attribution) = dbk:attribution
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:book] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:acknowledgements (dbk:acknowledgements) = dbk:acknowledgements *
+ + dbk:appendix (dbk:appendix) = dbk:appendix *
+ + dbk:article (dbk:article) = dbk:article *
+ + dbk:bibliography (dbk:bibliography) = dbk:bibliography *
+ + dbk:chapter (dbk:chapter) = dbk:chapter *
+ + dbk:colophon (dbk:colophon) = dbk:colophon *
+ + dbk:dedication (dbk:dedication) = dbk:dedication *
+ + dbk:glossary (dbk:glossary) = dbk:glossary *
+ + dbk:index (dbk:index) = dbk:index *
+ + dbk:part (dbk:part) = dbk:part *
+ + dbk:preface (dbk:preface) = dbk:preface *
+ + dbk:reference (dbk:reference) = dbk:reference *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ + dbk:toc (dbk:toc) = dbk:toc *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:bridgehead] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:otherrenderas (String) 
+ - dbk:renderas (String) 
+
+[dbk:callout] > argeodbk:base, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:arearefs (String) 
+
+[dbk:calloutlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:callout (dbk:callout) = dbk:callout *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:caption] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+ - dbk:class (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:style (String) 
+ - dbk:title (String) 
+
+[dbk:caution] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:chapter] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:refentry (dbk:refentry) = dbk:refentry *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:sect1 (dbk:sect1) = dbk:sect1 *
+ + dbk:section (dbk:section) = dbk:section *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+
+[dbk:citation] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:citebiblioid] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+ - dbk:otherclass (String) 
+
+[dbk:citerefentry] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:manvolnum (dbk:manvolnum) = dbk:manvolnum
+ + dbk:refentrytitle (dbk:refentrytitle) = dbk:refentrytitle
+
+[dbk:citetitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:pubwork (String) 
+
+[dbk:city] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:classname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:classsynopsis] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:classsynopsisinfo (dbk:classsynopsisinfo) = dbk:classsynopsisinfo *
+ + dbk:constructorsynopsis (dbk:constructorsynopsis) = dbk:constructorsynopsis *
+ + dbk:destructorsynopsis (dbk:destructorsynopsis) = dbk:destructorsynopsis *
+ + dbk:fieldsynopsis (dbk:fieldsynopsis) = dbk:fieldsynopsis *
+ + dbk:methodsynopsis (dbk:methodsynopsis) = dbk:methodsynopsis *
+ + dbk:ooclass (dbk:ooclass) = dbk:ooclass *
+ + dbk:ooexception (dbk:ooexception) = dbk:ooexception *
+ + dbk:oointerface (dbk:oointerface) = dbk:oointerface *
+ - dbk:class (String) 
+ - dbk:language (String) 
+
+[dbk:classsynopsisinfo] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:co (dbk:co) = dbk:co *
+ + dbk:info (dbk:info) = dbk:info *
+ + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ - dbk:continuation (String) 
+ - dbk:language (String) 
+ - dbk:linenumbering (String) 
+ - dbk:startinglinenumber (String) 
+ - xml:space (String) 
+
+[dbk:cmdsynopsis] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:arg (dbk:arg) = dbk:arg *
+ + dbk:command (dbk:command) = dbk:command *
+ + dbk:group (dbk:group) = dbk:group *
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:sbr (dbk:sbr) = dbk:sbr *
+ + dbk:synopfragment (dbk:synopfragment) = dbk:synopfragment *
+ - dbk:cmdlength (String) 
+ - dbk:label (String) 
+ - dbk:sepchar (String) 
+
+[dbk:co] > argeodbk:base
+ - dbk:label (String) 
+ - dbk:linkends (String) 
+
+[dbk:code] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:classname (dbk:classname) = dbk:classname *
+ + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname *
+ + dbk:function (dbk:function) = dbk:function *
+ + dbk:initializer (dbk:initializer) = dbk:initializer *
+ + dbk:interfacename (dbk:interfacename) = dbk:interfacename *
+ + dbk:methodname (dbk:methodname) = dbk:methodname *
+ + dbk:modifier (dbk:modifier) = dbk:modifier *
+ + dbk:ooclass (dbk:ooclass) = dbk:ooclass *
+ + dbk:ooexception (dbk:ooexception) = dbk:ooexception *
+ + dbk:oointerface (dbk:oointerface) = dbk:oointerface *
+ + dbk:parameter (dbk:parameter) = dbk:parameter *
+ + dbk:returnvalue (dbk:returnvalue) = dbk:returnvalue *
+ + dbk:type (dbk:type) = dbk:type *
+ + dbk:varname (dbk:varname) = dbk:varname *
+ - dbk:language (String) 
+
+[dbk:col] > nt:base
+ - dbk:align (String) 
+ - dbk:annotations (String) 
+ - dbk:arch (String) 
+ - dbk:audience (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:class (String) 
+ - dbk:condition (String) 
+ - dbk:conformance (String) 
+ - dbk:dir (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:os (String) 
+ - dbk:remap (String) 
+ - dbk:revision (String) 
+ - dbk:revisionflag (String) 
+ - dbk:security (String) 
+ - dbk:span (String) 
+ - dbk:style (String) 
+ - dbk:title (String) 
+ - dbk:userlevel (String) 
+ - dbk:valign (String) 
+ - dbk:vendor (String) 
+ - dbk:version (String) 
+ - dbk:width (String) 
+ - dbk:wordsize (String) 
+ - dbk:xreflabel (String) 
+ - xml:base (String) 
+ - xml:id (String) 
+ - xml:lang (String) 
+
+[dbk:colgroup] > nt:base
+ + dbk:col (dbk:col) = dbk:col *
+ - dbk:align (String) 
+ - dbk:annotations (String) 
+ - dbk:arch (String) 
+ - dbk:audience (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:class (String) 
+ - dbk:condition (String) 
+ - dbk:conformance (String) 
+ - dbk:dir (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:os (String) 
+ - dbk:remap (String) 
+ - dbk:revision (String) 
+ - dbk:revisionflag (String) 
+ - dbk:security (String) 
+ - dbk:span (String) 
+ - dbk:style (String) 
+ - dbk:title (String) 
+ - dbk:userlevel (String) 
+ - dbk:valign (String) 
+ - dbk:vendor (String) 
+ - dbk:version (String) 
+ - dbk:width (String) 
+ - dbk:wordsize (String) 
+ - dbk:xreflabel (String) 
+ - xml:base (String) 
+ - xml:id (String) 
+ - xml:lang (String) 
+
+[dbk:collab] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:affiliation (dbk:affiliation) = dbk:affiliation *
+ + dbk:org (dbk:org) = dbk:org *
+ + dbk:orgname (dbk:orgname) = dbk:orgname *
+ + dbk:person (dbk:person) = dbk:person *
+ + dbk:personname (dbk:personname) = dbk:personname *
+
+[dbk:colophon] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:colspec] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:colname (String) 
+ - dbk:colnum (String) 
+ - dbk:colsep (String) 
+ - dbk:colwidth (String) 
+ - dbk:rowsep (String) 
+
+[dbk:command] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:computeroutput] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:ubiquitousInlines
+ + dbk:co (dbk:co) = dbk:co *
+ + dbk:command (dbk:command) = dbk:command *
+ + dbk:computeroutput (dbk:computeroutput) = dbk:computeroutput *
+ + dbk:envar (dbk:envar) = dbk:envar *
+ + dbk:filename (dbk:filename) = dbk:filename *
+ + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal *
+ + dbk:option (dbk:option) = dbk:option *
+ + dbk:optional (dbk:optional) = dbk:optional *
+ + dbk:package (dbk:package) = dbk:package *
+ + dbk:parameter (dbk:parameter) = dbk:parameter *
+ + dbk:prompt (dbk:prompt) = dbk:prompt *
+ + dbk:property (dbk:property) = dbk:property *
+ + dbk:replaceable (dbk:replaceable) = dbk:replaceable *
+ + dbk:systemitem (dbk:systemitem) = dbk:systemitem *
+ + dbk:termdef (dbk:termdef) = dbk:termdef *
+ + dbk:userinput (dbk:userinput) = dbk:userinput *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:confdates] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:confgroup] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:confdates (dbk:confdates) = dbk:confdates *
+ + dbk:confnum (dbk:confnum) = dbk:confnum *
+ + dbk:confsponsor (dbk:confsponsor) = dbk:confsponsor *
+ + dbk:conftitle (dbk:conftitle) = dbk:conftitle *
+
+[dbk:confnum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:confsponsor] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:conftitle] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:constant] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+
+[dbk:constraint] > argeodbk:base, argeodbk:linkingAttributes
+
+[dbk:constraintdef] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:constructorsynopsis] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname *
+ + dbk:methodname (dbk:methodname) = dbk:methodname
+ + dbk:methodparam (dbk:methodparam) = dbk:methodparam *
+ + dbk:modifier (dbk:modifier) = dbk:modifier *
+ + dbk:void (dbk:void) = dbk:void
+ - dbk:language (String) 
+
+[dbk:contractnum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:contractsponsor] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:contrib] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:copyright] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:holder (dbk:holder) = dbk:holder *
+ + dbk:year (dbk:year) = dbk:year *
+
+[dbk:coref] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:label (String) 
+
+[dbk:country] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:cover] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:classsynopsis (dbk:classsynopsis) = dbk:classsynopsis *
+ + dbk:cmdsynopsis (dbk:cmdsynopsis) = dbk:cmdsynopsis *
+ + dbk:constraintdef (dbk:constraintdef) = dbk:constraintdef *
+ + dbk:constructorsynopsis (dbk:constructorsynopsis) = dbk:constructorsynopsis *
+ + dbk:destructorsynopsis (dbk:destructorsynopsis) = dbk:destructorsynopsis *
+ + dbk:fieldsynopsis (dbk:fieldsynopsis) = dbk:fieldsynopsis *
+ + dbk:funcsynopsis (dbk:funcsynopsis) = dbk:funcsynopsis *
+ + dbk:informalequation (dbk:informalequation) = dbk:informalequation *
+ + dbk:informalexample (dbk:informalexample) = dbk:informalexample *
+ + dbk:informalfigure (dbk:informalfigure) = dbk:informalfigure *
+ + dbk:informaltable (dbk:informaltable) = dbk:informaltable *
+ + dbk:literallayout (dbk:literallayout) = dbk:literallayout *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:methodsynopsis (dbk:methodsynopsis) = dbk:methodsynopsis *
+ + dbk:msgset (dbk:msgset) = dbk:msgset *
+ + dbk:productionset (dbk:productionset) = dbk:productionset *
+ + dbk:programlisting (dbk:programlisting) = dbk:programlisting *
+ + dbk:programlistingco (dbk:programlistingco) = dbk:programlistingco *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screen (dbk:screen) = dbk:screen *
+ + dbk:screenco (dbk:screenco) = dbk:screenco *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:synopsis (dbk:synopsis) = dbk:synopsis *
+ + dbk:task (dbk:task) = dbk:task *
+
+[dbk:database] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+
+[dbk:date] > argeodbk:base, argeodbk:linkingAttributes
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:dedication] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:destructorsynopsis] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname *
+ + dbk:methodname (dbk:methodname) = dbk:methodname
+ + dbk:methodparam (dbk:methodparam) = dbk:methodparam *
+ + dbk:modifier (dbk:modifier) = dbk:modifier *
+ + dbk:void (dbk:void) = dbk:void
+ - dbk:language (String) 
+
+[dbk:edition] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:editor] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:affiliation (dbk:affiliation) = dbk:affiliation *
+ + dbk:contrib (dbk:contrib) = dbk:contrib *
+ + dbk:email (dbk:email) = dbk:email *
+ + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
+ + dbk:orgname (dbk:orgname) = dbk:orgname
+ + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
+ + dbk:personname (dbk:personname) = dbk:personname
+ + dbk:uri (dbk:uri) = dbk:uri *
+
+[dbk:email] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:emphasis] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:entry] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:colname (String) 
+ - dbk:colsep (String) 
+ - dbk:morerows (String) 
+ - dbk:nameend (String) 
+ - dbk:namest (String) 
+ - dbk:rotate (String) 
+ - dbk:rowsep (String) 
+ - dbk:spanname (String) 
+ - dbk:valign (String) 
+
+[dbk:entrytbl] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:colspec (dbk:colspec) = dbk:colspec *
+ + dbk:spanspec (dbk:spanspec) = dbk:spanspec *
+ + dbk:tbody (dbk:tbody) = dbk:tbody
+ + dbk:thead (dbk:thead) = dbk:thead
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:colname (String) 
+ - dbk:cols (String) 
+ - dbk:colsep (String) 
+ - dbk:nameend (String) 
+ - dbk:namest (String) 
+ - dbk:rowsep (String) 
+ - dbk:spanname (String) 
+ - dbk:tgroupstyle (String) 
+
+[dbk:envar] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:epigraph] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:attribution (dbk:attribution) = dbk:attribution
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:literallayout (dbk:literallayout) = dbk:literallayout *
+
+[dbk:equation] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:alt (dbk:alt) = dbk:alt
+ + dbk:caption (dbk:caption) = dbk:caption
+ + dbk:mathphrase (dbk:mathphrase) = dbk:mathphrase *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ - dbk:floatstyle (String) 
+ - dbk:label (String) 
+ - dbk:pgwide (String) 
+
+[dbk:errorcode] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:errorname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:errortext] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:errortype] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:example] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:caption (dbk:caption) = dbk:caption
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:floatstyle (String) 
+ - dbk:label (String) 
+ - dbk:pgwide (String) 
+ - dbk:width (String) 
+
+[dbk:exceptionname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:extendedlink] > argeodbk:base
+ + dbk:arc (dbk:arc) = dbk:arc *
+ + dbk:locator (dbk:locator) = dbk:locator *
+
+[dbk:fax] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:fieldsynopsis] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:initializer (dbk:initializer) = dbk:initializer
+ + dbk:modifier (dbk:modifier) = dbk:modifier *
+ + dbk:type (dbk:type) = dbk:type
+ + dbk:varname (dbk:varname) = dbk:varname
+ - dbk:language (String) 
+
+[dbk:figure] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:caption (dbk:caption) = dbk:caption
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:floatstyle (String) 
+ - dbk:label (String) 
+ - dbk:pgwide (String) 
+
+[dbk:filename] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+ - dbk:path (String) 
+
+[dbk:firstname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:firstterm] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:baseform (String) 
+
+[dbk:footnote] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:label (String) 
+
+[dbk:footnoteref] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:label (String) 
+
+[dbk:foreignphrase] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:publishingInlines
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:application (dbk:application) = dbk:application *
+ + dbk:biblioref (dbk:biblioref) = dbk:biblioref *
+ + dbk:database (dbk:database) = dbk:database *
+ + dbk:hardware (dbk:hardware) = dbk:hardware *
+ + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
+ + dbk:link (dbk:link) = dbk:link *
+ + dbk:olink (dbk:olink) = dbk:olink *
+ + dbk:phrase (dbk:phrase) = dbk:phrase *
+ + dbk:productname (dbk:productname) = dbk:productname *
+ + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
+ + dbk:subscript (dbk:subscript) = dbk:subscript *
+ + dbk:superscript (dbk:superscript) = dbk:superscript *
+ + dbk:trademark (dbk:trademark) = dbk:trademark *
+ + dbk:xref (dbk:xref) = dbk:xref *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:formalpara] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:para (dbk:para) = dbk:para
+
+[dbk:funcdef] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:function (dbk:function) = dbk:function *
+ + dbk:type (dbk:type) = dbk:type *
+
+[dbk:funcparams] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:funcprototype] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:funcdef (dbk:funcdef) = dbk:funcdef
+ + dbk:modifier (dbk:modifier) = dbk:modifier *
+ + dbk:paramdef (dbk:paramdef) = dbk:paramdef *
+ + dbk:varargs (dbk:varargs) = dbk:varargs
+ + dbk:varargs (dbk:varargs) = dbk:varargs
+ + dbk:void (dbk:void) = dbk:void
+
+[dbk:funcsynopsis] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:funcprototype (dbk:funcprototype) = dbk:funcprototype *
+ + dbk:funcsynopsisinfo (dbk:funcsynopsisinfo) = dbk:funcsynopsisinfo *
+ + dbk:info (dbk:info) = dbk:info
+ - dbk:language (String) 
+
+[dbk:funcsynopsisinfo] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:co (dbk:co) = dbk:co *
+ + dbk:info (dbk:info) = dbk:info *
+ + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ - dbk:continuation (String) 
+ - dbk:language (String) 
+ - dbk:linenumbering (String) 
+ - dbk:startinglinenumber (String) 
+ - xml:space (String) 
+
+[dbk:function] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:glossary] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bibliography (dbk:bibliography) = dbk:bibliography
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:glossdiv (dbk:glossdiv) = dbk:glossdiv *
+ + dbk:glossentry (dbk:glossentry) = dbk:glossentry *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:glossdef] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:glossseealso (dbk:glossseealso) = dbk:glossseealso *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:subject (String) 
+
+[dbk:glossdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:glossentry (dbk:glossentry) = dbk:glossentry *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:glossentry] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes
+ + dbk:abbrev (dbk:abbrev) = dbk:abbrev
+ + dbk:acronym (dbk:acronym) = dbk:acronym
+ + dbk:glossdef (dbk:glossdef) = dbk:glossdef *
+ + dbk:glosssee (dbk:glosssee) = dbk:glosssee
+ + dbk:glossterm (dbk:glossterm) = dbk:glossterm
+ - dbk:sortas (String) 
+
+[dbk:glosslist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:glossentry (dbk:glossentry) = dbk:glossentry *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:glosssee] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:otherterm (Reference) 
+
+[dbk:glossseealso] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:otherterm (Reference) 
+
+[dbk:glossterm] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:baseform (String) 
+
+[dbk:group] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:arg (dbk:arg) = dbk:arg *
+ + dbk:group (dbk:group) = dbk:group *
+ + dbk:option (dbk:option) = dbk:option *
+ + dbk:replaceable (dbk:replaceable) = dbk:replaceable *
+ + dbk:sbr (dbk:sbr) = dbk:sbr *
+ + dbk:synopfragmentref (dbk:synopfragmentref) = dbk:synopfragmentref *
+ - dbk:choice (String) 
+ - dbk:rep (String) 
+
+[dbk:guibutton] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:accel (dbk:accel) = dbk:accel *
+
+[dbk:guiicon] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:accel (dbk:accel) = dbk:accel *
+
+[dbk:guilabel] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:accel (dbk:accel) = dbk:accel *
+
+[dbk:guimenu] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:accel (dbk:accel) = dbk:accel *
+
+[dbk:guimenuitem] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:accel (dbk:accel) = dbk:accel *
+
+[dbk:guisubmenu] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:accel (dbk:accel) = dbk:accel *
+
+[dbk:hardware] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:holder] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:honorific] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:imagedata] > argeodbk:base
+ + dbk:info (dbk:info) = dbk:info
+ - dbk:align (String) 
+ - dbk:contentdepth (String) 
+ - dbk:contentwidth (String) 
+ - dbk:depth (String) 
+ - dbk:entityref (String) 
+ - dbk:fileref (String) 
+ - dbk:format (String) 
+ - dbk:scale (String) 
+ - dbk:scalefit (String) 
+ - dbk:valign (String) 
+ - dbk:width (String) 
+
+[dbk:imageobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:imagedata (dbk:imagedata) = dbk:imagedata
+ + dbk:info (dbk:info) = dbk:info
+
+[dbk:imageobjectco] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:areaspec (dbk:areaspec) = dbk:areaspec
+ + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist *
+ + dbk:imageobject (dbk:imageobject) = dbk:imageobject *
+ + dbk:info (dbk:info) = dbk:info
+
+[dbk:important] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:index] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:indexdiv (dbk:indexdiv) = dbk:indexdiv *
+ + dbk:indexentry (dbk:indexentry) = dbk:indexentry *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+ - dbk:type (String) 
+
+[dbk:indexdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:indexentry (dbk:indexentry) = dbk:indexentry *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:indexentry] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:primaryie (dbk:primaryie) = dbk:primaryie
+ + dbk:secondaryie (dbk:secondaryie) = dbk:secondaryie *
+ + dbk:seealsoie (dbk:seealsoie) = dbk:seealsoie *
+ + dbk:seeie (dbk:seeie) = dbk:seeie *
+ + dbk:tertiaryie (dbk:tertiaryie) = dbk:tertiaryie *
+
+[dbk:indexterm] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:primary (dbk:primary) = dbk:primary
+ + dbk:secondary (dbk:secondary) = dbk:secondary
+ + dbk:see (dbk:see) = dbk:see
+ + dbk:seealso (dbk:seealso) = dbk:seealso *
+ + dbk:tertiary (dbk:tertiary) = dbk:tertiary
+ - dbk:class (String) 
+ - dbk:pagenum (String) 
+ - dbk:scope (String) 
+ - dbk:significance (String) 
+ - dbk:startref (Reference) 
+ - dbk:type (String) 
+ - dbk:zone (String) 
+
+[dbk:info] > argeodbk:base
+ + dbk:abstract (dbk:abstract) = dbk:abstract *
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums *
+ + dbk:author (dbk:author) = dbk:author *
+ + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup *
+ + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials *
+ + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage *
+ + dbk:biblioid (dbk:biblioid) = dbk:biblioid *
+ + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc *
+ + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset *
+ + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation *
+ + dbk:biblioset (dbk:biblioset) = dbk:biblioset *
+ + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource *
+ + dbk:collab (dbk:collab) = dbk:collab *
+ + dbk:confgroup (dbk:confgroup) = dbk:confgroup *
+ + dbk:contractnum (dbk:contractnum) = dbk:contractnum *
+ + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor *
+ + dbk:copyright (dbk:copyright) = dbk:copyright *
+ + dbk:cover (dbk:cover) = dbk:cover *
+ + dbk:date (dbk:date) = dbk:date *
+ + dbk:edition (dbk:edition) = dbk:edition *
+ + dbk:editor (dbk:editor) = dbk:editor *
+ + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink *
+ + dbk:issuenum (dbk:issuenum) = dbk:issuenum *
+ + dbk:itermset (dbk:itermset) = dbk:itermset *
+ + dbk:keywordset (dbk:keywordset) = dbk:keywordset *
+ + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:org (dbk:org) = dbk:org *
+ + dbk:orgname (dbk:orgname) = dbk:orgname *
+ + dbk:othercredit (dbk:othercredit) = dbk:othercredit *
+ + dbk:pagenums (dbk:pagenums) = dbk:pagenums *
+ + dbk:printhistory (dbk:printhistory) = dbk:printhistory *
+ + dbk:productname (dbk:productname) = dbk:productname *
+ + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
+ + dbk:pubdate (dbk:pubdate) = dbk:pubdate *
+ + dbk:publisher (dbk:publisher) = dbk:publisher *
+ + dbk:publishername (dbk:publishername) = dbk:publishername *
+ + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums *
+ + dbk:subjectset (dbk:subjectset) = dbk:subjectset *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ + dbk:title (dbk:title) = dbk:title *
+ + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev *
+ + dbk:volumenum (dbk:volumenum) = dbk:volumenum *
+
+[dbk:informalequation] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:alt (dbk:alt) = dbk:alt
+ + dbk:caption (dbk:caption) = dbk:caption
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:mathphrase (dbk:mathphrase) = dbk:mathphrase *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+
+[dbk:informalexample] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:caption (dbk:caption) = dbk:caption
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:floatstyle (String) 
+ - dbk:width (String) 
+
+[dbk:informalfigure] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:caption (dbk:caption) = dbk:caption
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:floatstyle (String) 
+ - dbk:label (String) 
+ - dbk:pgwide (String) 
+
+[dbk:informaltable] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:col (dbk:col) = dbk:col *
+ + dbk:colgroup (dbk:colgroup) = dbk:colgroup *
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:tbody (dbk:tbody) = dbk:tbody *
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ + dbk:tfoot (dbk:tfoot) = dbk:tfoot
+ + dbk:tgroup (dbk:tgroup) = dbk:tgroup *
+ + dbk:thead (dbk:thead) = dbk:thead
+ + dbk:tr (dbk:tr) = dbk:tr *
+ - dbk:border (String) 
+ - dbk:cellpadding (String) 
+ - dbk:cellspacing (String) 
+ - dbk:class (String) 
+ - dbk:colsep (String) 
+ - dbk:floatstyle (String) 
+ - dbk:frame (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:orient (String) 
+ - dbk:pgwide (String) 
+ - dbk:rowheader (String) 
+ - dbk:rowsep (String) 
+ - dbk:rules (String) 
+ - dbk:style (String) 
+ - dbk:summary (String) 
+ - dbk:tabstyle (String) 
+ - dbk:title (String) 
+ - dbk:width (String) 
+
+[dbk:initializer] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:inlineequation] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:alt (dbk:alt) = dbk:alt
+ + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
+ + dbk:mathphrase (dbk:mathphrase) = dbk:mathphrase *
+
+[dbk:inlinemediaobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:alt (dbk:alt) = dbk:alt
+ + dbk:audioobject (dbk:audioobject) = dbk:audioobject *
+ + dbk:imageobject (dbk:imageobject) = dbk:imageobject *
+ + dbk:imageobjectco (dbk:imageobjectco) = dbk:imageobjectco *
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ + dbk:videoobject (dbk:videoobject) = dbk:videoobject *
+
+[dbk:interfacename] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:issuenum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:itemizedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:listitem (dbk:listitem) = dbk:listitem *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:mark (String) 
+ - dbk:spacing (String) 
+
+[dbk:itermset] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes
+
+[dbk:jobtitle] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:keycap] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:function (String) 
+ - dbk:otherfunction (String) 
+
+[dbk:keycode] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:keycombo] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:keycap (dbk:keycap) = dbk:keycap *
+ + dbk:keycombo (dbk:keycombo) = dbk:keycombo *
+ + dbk:keysym (dbk:keysym) = dbk:keysym *
+ + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton *
+ - dbk:action (String) 
+ - dbk:otheraction (String) 
+
+[dbk:keysym] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:keyword] > argeodbk:base, argeodbk:linkingAttributes
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:keywordset] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:keyword (dbk:keyword) = dbk:keyword *
+
+[dbk:label] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:legalnotice] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:lhs] > argeodbk:base, argeodbk:linkingAttributes
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:lineage] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:lineannotation] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:link] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:endterm (Reference) 
+ - dbk:xrefstyle (String) 
+
+[dbk:listitem] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:override (String) 
+
+[dbk:literal] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:literallayout] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:co (dbk:co) = dbk:co *
+ + dbk:info (dbk:info) = dbk:info *
+ + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ - dbk:class (String) 
+ - dbk:continuation (String) 
+ - dbk:language (String) 
+ - dbk:linenumbering (String) 
+ - dbk:startinglinenumber (String) 
+ - xml:space (String) 
+
+[dbk:locator] > argeodbk:base
+ - xlink:label (String) 
+
+[dbk:manvolnum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:markup] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:mathphrase] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:emphasis (dbk:emphasis) = dbk:emphasis *
+
+[dbk:mediaobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:alt (dbk:alt) = dbk:alt
+ + dbk:audioobject (dbk:audioobject) = dbk:audioobject *
+ + dbk:caption (dbk:caption) = dbk:caption
+ + dbk:imageobject (dbk:imageobject) = dbk:imageobject *
+ + dbk:imageobjectco (dbk:imageobjectco) = dbk:imageobjectco *
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ + dbk:videoobject (dbk:videoobject) = dbk:videoobject *
+
+[dbk:member] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:menuchoice] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:guibutton (dbk:guibutton) = dbk:guibutton *
+ + dbk:guiicon (dbk:guiicon) = dbk:guiicon *
+ + dbk:guilabel (dbk:guilabel) = dbk:guilabel *
+ + dbk:guimenu (dbk:guimenu) = dbk:guimenu *
+ + dbk:guimenuitem (dbk:guimenuitem) = dbk:guimenuitem *
+ + dbk:guisubmenu (dbk:guisubmenu) = dbk:guisubmenu *
+ + dbk:shortcut (dbk:shortcut) = dbk:shortcut
+
+[dbk:methodname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:methodparam] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:funcparams (dbk:funcparams) = dbk:funcparams
+ + dbk:initializer (dbk:initializer) = dbk:initializer
+ + dbk:modifier (dbk:modifier) = dbk:modifier *
+ + dbk:modifier (dbk:modifier) = dbk:modifier *
+ + dbk:parameter (dbk:parameter) = dbk:parameter
+ + dbk:type (dbk:type) = dbk:type *
+ - dbk:choice (String) 
+ - dbk:rep (String) 
+
+[dbk:methodsynopsis] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname *
+ + dbk:methodname (dbk:methodname) = dbk:methodname
+ + dbk:methodparam (dbk:methodparam) = dbk:methodparam *
+ + dbk:modifier (dbk:modifier) = dbk:modifier *
+ + dbk:type (dbk:type) = dbk:type
+ + dbk:void (dbk:void) = dbk:void
+ - dbk:language (String) 
+
+[dbk:modifier] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - xml:space (String) 
+
+[dbk:mousebutton] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:msg] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:msgmain (dbk:msgmain) = dbk:msgmain
+ + dbk:msgrel (dbk:msgrel) = dbk:msgrel *
+ + dbk:msgsub (dbk:msgsub) = dbk:msgsub *
+
+[dbk:msgaud] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:msgentry] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:msg (dbk:msg) = dbk:msg *
+ + dbk:msgexplan (dbk:msgexplan) = dbk:msgexplan *
+ + dbk:msginfo (dbk:msginfo) = dbk:msginfo
+
+[dbk:msgexplan] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:msginfo] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:msgaud (dbk:msgaud) = dbk:msgaud *
+ + dbk:msglevel (dbk:msglevel) = dbk:msglevel *
+ + dbk:msgorig (dbk:msgorig) = dbk:msgorig *
+
+[dbk:msglevel] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:msgmain] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:msgtext (dbk:msgtext) = dbk:msgtext
+
+[dbk:msgorig] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:msgrel] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:msgtext (dbk:msgtext) = dbk:msgtext
+
+[dbk:msgset] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:msgentry (dbk:msgentry) = dbk:msgentry *
+ + dbk:simplemsgentry (dbk:simplemsgentry) = dbk:simplemsgentry *
+
+[dbk:msgsub] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:msgtext (dbk:msgtext) = dbk:msgtext
+
+[dbk:msgtext] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:nonterminal] > argeodbk:base, argeodbk:linkingAttributes
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+ - dbk:def (String) 
+
+[dbk:note] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:olink] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:localinfo (String) 
+ - dbk:targetdoc (String) 
+ - dbk:targetptr (String) 
+ - dbk:type (String) 
+ - dbk:xrefstyle (String) 
+
+[dbk:ooclass] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:classname (dbk:classname) = dbk:classname
+ + dbk:modifier (dbk:modifier) = dbk:modifier *
+ + dbk:package (dbk:package) = dbk:package *
+
+[dbk:ooexception] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname
+ + dbk:modifier (dbk:modifier) = dbk:modifier *
+ + dbk:package (dbk:package) = dbk:package *
+
+[dbk:oointerface] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:interfacename (dbk:interfacename) = dbk:interfacename
+ + dbk:modifier (dbk:modifier) = dbk:modifier *
+ + dbk:package (dbk:package) = dbk:package *
+
+[dbk:option] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:optional] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:orderedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:listitem (dbk:listitem) = dbk:listitem *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:continuation (String) 
+ - dbk:inheritnum (String) 
+ - dbk:numeration (String) 
+ - dbk:spacing (String) 
+ - dbk:startingnumber (String) 
+
+[dbk:org] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:affiliation (dbk:affiliation) = dbk:affiliation *
+ + dbk:email (dbk:email) = dbk:email *
+ + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
+ + dbk:orgname (dbk:orgname) = dbk:orgname
+ + dbk:uri (dbk:uri) = dbk:uri *
+
+[dbk:orgdiv] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:orgname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+ - dbk:otherclass (String) 
+
+[dbk:otheraddr] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:othercredit] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:affiliation (dbk:affiliation) = dbk:affiliation *
+ + dbk:contrib (dbk:contrib) = dbk:contrib *
+ + dbk:email (dbk:email) = dbk:email *
+ + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
+ + dbk:orgname (dbk:orgname) = dbk:orgname
+ + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
+ + dbk:personname (dbk:personname) = dbk:personname
+ + dbk:uri (dbk:uri) = dbk:uri *
+ - dbk:class (String) 
+ - dbk:otherclass (String) 
+
+[dbk:othername] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:package] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:pagenums] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:para] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:info (dbk:info) = dbk:info *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:paramdef] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:funcparams (dbk:funcparams) = dbk:funcparams *
+ + dbk:initializer (dbk:initializer) = dbk:initializer *
+ + dbk:parameter (dbk:parameter) = dbk:parameter *
+ + dbk:type (dbk:type) = dbk:type *
+ - dbk:choice (String) 
+
+[dbk:parameter] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+
+[dbk:part] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:acknowledgements (dbk:acknowledgements) = dbk:acknowledgements *
+ + dbk:appendix (dbk:appendix) = dbk:appendix *
+ + dbk:article (dbk:article) = dbk:article *
+ + dbk:bibliography (dbk:bibliography) = dbk:bibliography *
+ + dbk:chapter (dbk:chapter) = dbk:chapter *
+ + dbk:colophon (dbk:colophon) = dbk:colophon *
+ + dbk:dedication (dbk:dedication) = dbk:dedication *
+ + dbk:glossary (dbk:glossary) = dbk:glossary *
+ + dbk:index (dbk:index) = dbk:index *
+ + dbk:partintro (dbk:partintro) = dbk:partintro
+ + dbk:preface (dbk:preface) = dbk:preface *
+ + dbk:refentry (dbk:refentry) = dbk:refentry *
+ + dbk:reference (dbk:reference) = dbk:reference *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ + dbk:toc (dbk:toc) = dbk:toc *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:partintro] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:refentry (dbk:refentry) = dbk:refentry *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:sect1 (dbk:sect1) = dbk:sect1 *
+ + dbk:section (dbk:section) = dbk:section *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:person] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:affiliation (dbk:affiliation) = dbk:affiliation *
+ + dbk:email (dbk:email) = dbk:email *
+ + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
+ + dbk:personname (dbk:personname) = dbk:personname
+ + dbk:uri (dbk:uri) = dbk:uri *
+
+[dbk:personblurb] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+
+[dbk:personname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:firstname (dbk:firstname) = dbk:firstname *
+ + dbk:honorific (dbk:honorific) = dbk:honorific *
+ + dbk:lineage (dbk:lineage) = dbk:lineage *
+ + dbk:othername (dbk:othername) = dbk:othername *
+ + dbk:surname (dbk:surname) = dbk:surname *
+
+[dbk:phone] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:phrase] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:pob] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:postcode] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:preface] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:refentry (dbk:refentry) = dbk:refentry *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:sect1 (dbk:sect1) = dbk:sect1 *
+ + dbk:section (dbk:section) = dbk:section *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+
+[dbk:primary] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:sortas (String) 
+
+[dbk:primaryie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:linkends (String) 
+
+[dbk:printhistory] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+
+[dbk:procedure] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:step (dbk:step) = dbk:step *
+
+[dbk:production] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:constraint (dbk:constraint) = dbk:constraint *
+ + dbk:lhs (dbk:lhs) = dbk:lhs
+ + dbk:rhs (dbk:rhs) = dbk:rhs
+
+[dbk:productionrecap] > argeodbk:base, argeodbk:linkingAttributes
+
+[dbk:productionset] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:production (dbk:production) = dbk:production *
+ + dbk:productionrecap (dbk:productionrecap) = dbk:productionrecap *
+
+[dbk:productname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+
+[dbk:productnumber] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:programlisting] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:co (dbk:co) = dbk:co *
+ + dbk:info (dbk:info) = dbk:info *
+ + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ - dbk:continuation (String) 
+ - dbk:language (String) 
+ - dbk:linenumbering (String) 
+ - dbk:startinglinenumber (String) 
+ - dbk:width (String) 
+ - xml:space (String) 
+
+[dbk:programlistingco] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:areaspec (dbk:areaspec) = dbk:areaspec
+ + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist *
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:programlisting (dbk:programlisting) = dbk:programlisting
+
+[dbk:prompt] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:co (dbk:co) = dbk:co *
+
+[dbk:property] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:pubdate] > argeodbk:base, argeodbk:linkingAttributes
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:publisher] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:address (dbk:address) = dbk:address *
+ + dbk:publishername (dbk:publishername) = dbk:publishername
+
+[dbk:publishername] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:qandadiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:qandadiv (dbk:qandadiv) = dbk:qandadiv *
+ + dbk:qandaentry (dbk:qandaentry) = dbk:qandaentry *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:qandaentry] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:answer (dbk:answer) = dbk:answer *
+ + dbk:question (dbk:question) = dbk:question
+
+[dbk:qandaset] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:qandadiv (dbk:qandadiv) = dbk:qandadiv *
+ + dbk:qandaentry (dbk:qandaentry) = dbk:qandaentry *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:defaultlabel (String) 
+
+[dbk:question] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:label (dbk:label) = dbk:label
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:quote] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:refclass] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:application (dbk:application) = dbk:application *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:refdescriptor] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:refentry] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:refmeta (dbk:refmeta) = dbk:refmeta
+ + dbk:refnamediv (dbk:refnamediv) = dbk:refnamediv *
+ + dbk:refsect1 (dbk:refsect1) = dbk:refsect1 *
+ + dbk:refsection (dbk:refsection) = dbk:refsection *
+ + dbk:refsynopsisdiv (dbk:refsynopsisdiv) = dbk:refsynopsisdiv
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:refentrytitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:reference] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:partintro (dbk:partintro) = dbk:partintro
+ + dbk:refentry (dbk:refentry) = dbk:refentry *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:refmeta] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes
+ + dbk:manvolnum (dbk:manvolnum) = dbk:manvolnum
+ + dbk:refentrytitle (dbk:refentrytitle) = dbk:refentrytitle
+ + dbk:refmiscinfo (dbk:refmiscinfo) = dbk:refmiscinfo *
+
+[dbk:refmiscinfo] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+ - dbk:otherclass (String) 
+
+[dbk:refname] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:refnamediv] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:refclass (dbk:refclass) = dbk:refclass *
+ + dbk:refdescriptor (dbk:refdescriptor) = dbk:refdescriptor
+ + dbk:refname (dbk:refname) = dbk:refname *
+ + dbk:refpurpose (dbk:refpurpose) = dbk:refpurpose
+
+[dbk:refpurpose] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:refsect1] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:refsect2 (dbk:refsect2) = dbk:refsect2 *
+ + dbk:refsect2 (dbk:refsect2) = dbk:refsect2 *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:refsect2] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:refsect3 (dbk:refsect3) = dbk:refsect3 *
+ + dbk:refsect3 (dbk:refsect3) = dbk:refsect3 *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:refsect3] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:refsection] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:refsection (dbk:refsection) = dbk:refsection *
+ + dbk:refsection (dbk:refsection) = dbk:refsection *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:refsynopsisdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:refsect2 (dbk:refsect2) = dbk:refsect2 *
+ + dbk:refsection (dbk:refsection) = dbk:refsection *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+
+[dbk:releaseinfo] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:remark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:replaceable] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:co (dbk:co) = dbk:co *
+ - dbk:class (String) 
+
+[dbk:returnvalue] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:revdescription] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:revhistory] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:revision (dbk:revision) = dbk:revision *
+
+[dbk:revision] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:author (dbk:author) = dbk:author *
+ + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials *
+ + dbk:date (dbk:date) = dbk:date
+ + dbk:revdescription (dbk:revdescription) = dbk:revdescription
+ + dbk:revnumber (dbk:revnumber) = dbk:revnumber
+ + dbk:revremark (dbk:revremark) = dbk:revremark
+
+[dbk:revnumber] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:revremark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:rhs] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
+ + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal *
+ + dbk:sbr (dbk:sbr) = dbk:sbr *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:row] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:entry (dbk:entry) = dbk:entry *
+ + dbk:entrytbl (dbk:entrytbl) = dbk:entrytbl *
+ - dbk:rowsep (String) 
+ - dbk:valign (String) 
+
+[dbk:sbr] > argeodbk:base
+
+[dbk:screen] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:co (dbk:co) = dbk:co *
+ + dbk:info (dbk:info) = dbk:info *
+ + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ - dbk:continuation (String) 
+ - dbk:language (String) 
+ - dbk:linenumbering (String) 
+ - dbk:startinglinenumber (String) 
+ - dbk:width (String) 
+ - xml:space (String) 
+
+[dbk:screenco] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:areaspec (dbk:areaspec) = dbk:areaspec
+ + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist *
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:screen (dbk:screen) = dbk:screen
+
+[dbk:screenshot] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+
+[dbk:secondary] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:sortas (String) 
+
+[dbk:secondaryie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:linkends (String) 
+
+[dbk:sect1] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:sect2 (dbk:sect2) = dbk:sect2 *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+
+[dbk:sect2] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:sect3 (dbk:sect3) = dbk:sect3 *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+
+[dbk:sect3] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:sect4 (dbk:sect4) = dbk:sect4 *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+
+[dbk:sect4] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:sect5 (dbk:sect5) = dbk:sect5 *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+
+[dbk:sect5] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+
+[dbk:section] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:refentry (dbk:refentry) = dbk:refentry *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:section (dbk:section) = dbk:section *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+ + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
+
+[dbk:see] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:seealso] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:seealsoie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:linkends (String) 
+
+[dbk:seeie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:seg] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:seglistitem] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:seg (dbk:seg) = dbk:seg *
+
+[dbk:segmentedlist] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:seglistitem (dbk:seglistitem) = dbk:seglistitem *
+ + dbk:segtitle (dbk:segtitle) = dbk:segtitle *
+
+[dbk:segtitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:seriesvolnums] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:set] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:book (dbk:book) = dbk:book *
+ + dbk:set (dbk:set) = dbk:set *
+ + dbk:setindex (dbk:setindex) = dbk:setindex
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ + dbk:toc (dbk:toc) = dbk:toc
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:setindex] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:indexdiv (dbk:indexdiv) = dbk:indexdiv *
+ + dbk:indexentry (dbk:indexentry) = dbk:indexentry *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+ - dbk:type (String) 
+
+[dbk:shortaffil] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:shortcut] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:keycap (dbk:keycap) = dbk:keycap *
+ + dbk:keycombo (dbk:keycombo) = dbk:keycombo *
+ + dbk:keysym (dbk:keysym) = dbk:keysym *
+ + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton *
+ - dbk:action (String) 
+ - dbk:otheraction (String) 
+
+[dbk:sidebar] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:simpara] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:info (dbk:info) = dbk:info *
+
+[dbk:simplelist] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:member (dbk:member) = dbk:member *
+ - dbk:columns (String) 
+ - dbk:type (String) 
+
+[dbk:simplemsgentry] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:msgexplan (dbk:msgexplan) = dbk:msgexplan *
+ + dbk:msgtext (dbk:msgtext) = dbk:msgtext
+ - dbk:msgaud (String) 
+ - dbk:msglevel (String) 
+ - dbk:msgorig (String) 
+
+[dbk:simplesect] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:spanspec] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:colsep (String) 
+ - dbk:nameend (String) 
+ - dbk:namest (String) 
+ - dbk:rowsep (String) 
+ - dbk:spanname (String) 
+
+[dbk:state] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:step] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:stepalternatives (dbk:stepalternatives) = dbk:stepalternatives
+ + dbk:substeps (dbk:substeps) = dbk:substeps
+ - dbk:performance (String) 
+
+[dbk:stepalternatives] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:step (dbk:step) = dbk:step *
+ - dbk:performance (String) 
+
+[dbk:street] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:subject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:subjectterm (dbk:subjectterm) = dbk:subjectterm *
+ - dbk:weight (String) 
+
+[dbk:subjectset] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:subject (dbk:subject) = dbk:subject *
+ - dbk:scheme (String) 
+
+[dbk:subjectterm] > argeodbk:base, argeodbk:linkingAttributes
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:subscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:substeps] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:step (dbk:step) = dbk:step *
+ - dbk:performance (String) 
+
+[dbk:subtitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:superscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:surname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:symbol] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+
+[dbk:synopfragment] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:arg (dbk:arg) = dbk:arg *
+ + dbk:group (dbk:group) = dbk:group *
+
+[dbk:synopfragmentref] > argeodbk:base, argeodbk:linkingAttributes
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:synopsis] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:co (dbk:co) = dbk:co *
+ + dbk:info (dbk:info) = dbk:info *
+ + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ - dbk:continuation (String) 
+ - dbk:label (String) 
+ - dbk:language (String) 
+ - dbk:linenumbering (String) 
+ - dbk:startinglinenumber (String) 
+ - xml:space (String) 
+
+[dbk:systemitem] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ + dbk:co (dbk:co) = dbk:co *
+ - dbk:class (String) 
+
+[dbk:table] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:caption (dbk:caption) = dbk:caption
+ + dbk:col (dbk:col) = dbk:col *
+ + dbk:colgroup (dbk:colgroup) = dbk:colgroup *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:tbody (dbk:tbody) = dbk:tbody *
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ + dbk:tfoot (dbk:tfoot) = dbk:tfoot
+ + dbk:tgroup (dbk:tgroup) = dbk:tgroup *
+ + dbk:thead (dbk:thead) = dbk:thead
+ + dbk:tr (dbk:tr) = dbk:tr *
+ - dbk:border (String) 
+ - dbk:cellpadding (String) 
+ - dbk:cellspacing (String) 
+ - dbk:class (String) 
+ - dbk:colsep (String) 
+ - dbk:floatstyle (String) 
+ - dbk:frame (String) 
+ - dbk:label (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:orient (String) 
+ - dbk:pgwide (String) 
+ - dbk:rowheader (String) 
+ - dbk:rowsep (String) 
+ - dbk:rules (String) 
+ - dbk:shortentry (String) 
+ - dbk:style (String) 
+ - dbk:summary (String) 
+ - dbk:tabstyle (String) 
+ - dbk:title (String) 
+ - dbk:tocentry (String) 
+ - dbk:width (String) 
+
+[dbk:tag] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+ - dbk:namespace (String) 
+
+[dbk:task] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:example (dbk:example) = dbk:example *
+ + dbk:procedure (dbk:procedure) = dbk:procedure
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ + dbk:taskprerequisites (dbk:taskprerequisites) = dbk:taskprerequisites
+ + dbk:taskrelated (dbk:taskrelated) = dbk:taskrelated
+ + dbk:tasksummary (dbk:tasksummary) = dbk:tasksummary
+
+[dbk:taskprerequisites] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:taskrelated] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:tasksummary] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:tbody] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:row (dbk:row) = dbk:row *
+ + dbk:tr (dbk:tr) = dbk:tr *
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:class (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:style (String) 
+ - dbk:title (String) 
+ - dbk:valign (String) 
+
+[dbk:td] > argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:abbr (String) 
+ - dbk:align (String) 
+ - dbk:annotations (String) 
+ - dbk:arch (String) 
+ - dbk:audience (String) 
+ - dbk:axis (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:class (String) 
+ - dbk:colspan (String) 
+ - dbk:condition (String) 
+ - dbk:conformance (String) 
+ - dbk:dir (String) 
+ - dbk:headers (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:os (String) 
+ - dbk:remap (String) 
+ - dbk:revision (String) 
+ - dbk:revisionflag (String) 
+ - dbk:rowspan (String) 
+ - dbk:scope (String) 
+ - dbk:security (String) 
+ - dbk:style (String) 
+ - dbk:title (String) 
+ - dbk:userlevel (String) 
+ - dbk:valign (String) 
+ - dbk:vendor (String) 
+ - dbk:version (String) 
+ - dbk:wordsize (String) 
+ - dbk:xreflabel (String) 
+ - xml:base (String) 
+ - xml:id (String) 
+ - xml:lang (String) 
+
+[dbk:term] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:termdef] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:baseform (String) 
+ - dbk:sortas (String) 
+
+[dbk:tertiary] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:sortas (String) 
+
+[dbk:tertiaryie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:linkends (String) 
+
+[dbk:textdata] > argeodbk:base
+ + dbk:info (dbk:info) = dbk:info
+ - dbk:encoding (String) 
+ - dbk:entityref (String) 
+ - dbk:fileref (String) 
+ - dbk:format (String) 
+
+[dbk:textobject] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:phrase (dbk:phrase) = dbk:phrase
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:textdata (dbk:textdata) = dbk:textdata
+
+[dbk:tfoot] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:colspec (dbk:colspec) = dbk:colspec *
+ + dbk:row (dbk:row) = dbk:row *
+ + dbk:tr (dbk:tr) = dbk:tr *
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:class (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:style (String) 
+ - dbk:title (String) 
+ - dbk:valign (String) 
+
+[dbk:tgroup] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:colspec (dbk:colspec) = dbk:colspec *
+ + dbk:spanspec (dbk:spanspec) = dbk:spanspec *
+ + dbk:tbody (dbk:tbody) = dbk:tbody
+ + dbk:tfoot (dbk:tfoot) = dbk:tfoot
+ + dbk:thead (dbk:thead) = dbk:thead
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:cols (String) 
+ - dbk:colsep (String) 
+ - dbk:rowsep (String) 
+ - dbk:tgroupstyle (String) 
+
+[dbk:th] > argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ - dbk:abbr (String) 
+ - dbk:align (String) 
+ - dbk:annotations (String) 
+ - dbk:arch (String) 
+ - dbk:audience (String) 
+ - dbk:axis (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:class (String) 
+ - dbk:colspan (String) 
+ - dbk:condition (String) 
+ - dbk:conformance (String) 
+ - dbk:dir (String) 
+ - dbk:headers (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:os (String) 
+ - dbk:remap (String) 
+ - dbk:revision (String) 
+ - dbk:revisionflag (String) 
+ - dbk:rowspan (String) 
+ - dbk:scope (String) 
+ - dbk:security (String) 
+ - dbk:style (String) 
+ - dbk:title (String) 
+ - dbk:userlevel (String) 
+ - dbk:valign (String) 
+ - dbk:vendor (String) 
+ - dbk:version (String) 
+ - dbk:wordsize (String) 
+ - dbk:xreflabel (String) 
+ - xml:base (String) 
+ - xml:id (String) 
+ - xml:lang (String) 
+
+[dbk:thead] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:colspec (dbk:colspec) = dbk:colspec *
+ + dbk:row (dbk:row) = dbk:row *
+ + dbk:tr (dbk:tr) = dbk:tr *
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:class (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:style (String) 
+ - dbk:title (String) 
+ - dbk:valign (String) 
+
+[dbk:tip] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:title] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:titleabbrev] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:toc] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:tocdiv (dbk:tocdiv) = dbk:tocdiv *
+ + dbk:tocentry (dbk:tocentry) = dbk:tocentry *
+
+[dbk:tocdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ + dbk:tocdiv (dbk:tocdiv) = dbk:tocdiv *
+ + dbk:tocentry (dbk:tocentry) = dbk:tocentry *
+ - dbk:pagenum (String) 
+
+[dbk:tocentry] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:pagenum (String) 
+
+[dbk:token] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:tr] > nt:base
+ + dbk:td (dbk:td) = dbk:td *
+ + dbk:th (dbk:th) = dbk:th *
+ - dbk:align (String) 
+ - dbk:annotations (String) 
+ - dbk:arch (String) 
+ - dbk:audience (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:class (String) 
+ - dbk:condition (String) 
+ - dbk:conformance (String) 
+ - dbk:dir (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:os (String) 
+ - dbk:remap (String) 
+ - dbk:revision (String) 
+ - dbk:revisionflag (String) 
+ - dbk:security (String) 
+ - dbk:style (String) 
+ - dbk:title (String) 
+ - dbk:userlevel (String) 
+ - dbk:valign (String) 
+ - dbk:vendor (String) 
+ - dbk:version (String) 
+ - dbk:wordsize (String) 
+ - dbk:xreflabel (String) 
+ - xml:base (String) 
+ - xml:id (String) 
+ - xml:lang (String) 
+
+[dbk:trademark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+
+[dbk:type] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:uri] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:type (String) 
+
+[dbk:userinput] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:ubiquitousInlines
+ + dbk:accel (dbk:accel) = dbk:accel *
+ + dbk:co (dbk:co) = dbk:co *
+ + dbk:command (dbk:command) = dbk:command *
+ + dbk:computeroutput (dbk:computeroutput) = dbk:computeroutput *
+ + dbk:envar (dbk:envar) = dbk:envar *
+ + dbk:filename (dbk:filename) = dbk:filename *
+ + dbk:guibutton (dbk:guibutton) = dbk:guibutton *
+ + dbk:guiicon (dbk:guiicon) = dbk:guiicon *
+ + dbk:guilabel (dbk:guilabel) = dbk:guilabel *
+ + dbk:guimenu (dbk:guimenu) = dbk:guimenu *
+ + dbk:guimenuitem (dbk:guimenuitem) = dbk:guimenuitem *
+ + dbk:guisubmenu (dbk:guisubmenu) = dbk:guisubmenu *
+ + dbk:keycap (dbk:keycap) = dbk:keycap *
+ + dbk:keycode (dbk:keycode) = dbk:keycode *
+ + dbk:keycombo (dbk:keycombo) = dbk:keycombo *
+ + dbk:keysym (dbk:keysym) = dbk:keysym *
+ + dbk:menuchoice (dbk:menuchoice) = dbk:menuchoice *
+ + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton *
+ + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal *
+ + dbk:option (dbk:option) = dbk:option *
+ + dbk:optional (dbk:optional) = dbk:optional *
+ + dbk:package (dbk:package) = dbk:package *
+ + dbk:parameter (dbk:parameter) = dbk:parameter *
+ + dbk:prompt (dbk:prompt) = dbk:prompt *
+ + dbk:property (dbk:property) = dbk:property *
+ + dbk:replaceable (dbk:replaceable) = dbk:replaceable *
+ + dbk:shortcut (dbk:shortcut) = dbk:shortcut *
+ + dbk:systemitem (dbk:systemitem) = dbk:systemitem *
+ + dbk:termdef (dbk:termdef) = dbk:termdef *
+ + dbk:userinput (dbk:userinput) = dbk:userinput *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:varargs] > argeodbk:base, argeodbk:linkingAttributes
+
+[dbk:variablelist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+ + dbk:varlistentry (dbk:varlistentry) = dbk:varlistentry *
+ - dbk:spacing (String) 
+ - dbk:termlength (String) 
+
+[dbk:varlistentry] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:listitem (dbk:listitem) = dbk:listitem
+ + dbk:term (dbk:term) = dbk:term *
+
+[dbk:varname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:videodata] > argeodbk:base
+ + dbk:info (dbk:info) = dbk:info
+ - dbk:align (String) 
+ - dbk:contentdepth (String) 
+ - dbk:contentwidth (String) 
+ - dbk:depth (String) 
+ - dbk:entityref (String) 
+ - dbk:fileref (String) 
+ - dbk:format (String) 
+ - dbk:scale (String) 
+ - dbk:scalefit (String) 
+ - dbk:valign (String) 
+ - dbk:width (String) 
+
+[dbk:videoobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:videodata (dbk:videodata) = dbk:videodata
+
+[dbk:void] > argeodbk:base, argeodbk:linkingAttributes
+
+[dbk:volumenum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:warning] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
+ + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
+
+[dbk:wordasword] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:xmltext] > nt:base
+ - jcr:xmlcharacters (String) 
+
+[dbk:xref] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:endterm (Reference) 
+ - dbk:xrefstyle (String) 
+
+[dbk:year] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[xs:anyType] > nt:base
+ + * (nt:base) 
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+ - * (undefined) 
+
+
diff --git a/org.argeo.jcr/src/org/argeo/jcr/docbook/docbook.cnd b/org.argeo.jcr/src/org/argeo/jcr/docbook/docbook.cnd
new file mode 100644 (file)
index 0000000..c737cee
--- /dev/null
@@ -0,0 +1,531 @@
+<dbk = 'http://docbook.org/ns/docbook'>
+<argeodbk = 'http://www.argeo.org/ns/argeodbk'>
+<xlink = 'http://www.w3.org/1999/xlink'>
+
+[argeodbk:titled]
+mixin
+ + dbk:info (dbk:info) = dbk:info *
+ + dbk:title (dbk:title) = dbk:title *
+
+[argeodbk:linkingAttributes]
+mixin
+ - dbk:linkend (String)
+ - xlink:actuate (String)
+ - xlink:arcrole (String)
+ - xlink:href (String)
+ - xlink:role (String)
+ - xlink:show (String)
+ - xlink:title (String)
+ - xlink:type (String)
+
+[argeodbk:freeText]
+mixin
+ + dbk:phrase (dbk:phrase) = dbk:phrase *
+ + dbk:replaceable (dbk:replaceable) = dbk:replaceable *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[argeodbk:markupInlines]
+mixin
+
+[argeodbk:listElements]
+mixin
+ + dbk:itemizedlist (dbk:itemizedlist) = dbk:itemizedlist *
+ + dbk:orderedlist (dbk:orderedlist) = dbk:orderedlist *
+ + dbk:simplelist (dbk:simplelist) = dbk:simplelist *
+
+[argeodbk:paragraphElements]
+mixin
+ + dbk:para (dbk:para) = dbk:para *
+
+[argeodbk:indexingInlines]
+mixin
+
+[argeodbk:techDocElements]
+mixin
+ + dbk:table (dbk:table) = dbk:table *
+
+[argeodbk:techDocInlines]
+mixin
+
+[argeodbk:publishingElements]
+mixin
+
+[argeodbk:ubiquitousInlines]
+mixin
+ + dbk:alt (dbk:alt) = dbk:alt *
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:biblioref (dbk:biblioref) = dbk:biblioref *
+ + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
+ + dbk:link (dbk:link) = dbk:link *
+ + dbk:olink (dbk:olink) = dbk:olink *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:subscript (dbk:subscript) = dbk:subscript *
+ + dbk:superscript (dbk:superscript) = dbk:superscript *
+ + dbk:xref (dbk:xref) = dbk:xref *
+
+[argeodbk:abstractSection]
+mixin
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String)
+ - dbk:status (String)
+
+[argeodbk:bibliographyInlines]
+mixin
+ + dbk:author (dbk:author) = dbk:author *
+ + dbk:editor (dbk:editor) = dbk:editor *
+ + dbk:orgname (dbk:orgname) = dbk:orgname *
+ + dbk:personname (dbk:personname) = dbk:personname *
+
+[argeodbk:publishingInlines]
+mixin
+ + dbk:emphasis (dbk:emphasis) = dbk:emphasis *
+
+[argeodbk:base]
+abstract
+ - dbk:annotations (String)
+ - dbk:arch (String)
+ - dbk:audience (String)
+ - dbk:condition (String)
+ - dbk:conformance (String)
+ - dbk:dir (String)
+ - dbk:os (String)
+ - dbk:remap (String)
+ - dbk:revision (String)
+ - dbk:revisionflag (String)
+ - dbk:role (String)
+ - dbk:security (String)
+ - dbk:userlevel (String)
+ - dbk:vendor (String)
+ - dbk:version (String)
+ - dbk:wordsize (String)
+ - dbk:xreflabel (String)
+// - {http://www.w3.org/XML/1998/namespace}base (String)
+// - {http://www.w3.org/XML/1998/namespace}id (String)
+// - {http://www.w3.org/XML/1998/namespace}lang (String)
+
+[dbk:alt] > argeodbk:base
+ + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:anchor] > argeodbk:base
+
+[dbk:annotation] > argeodbk:base, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ - dbk:annotates (String) 
+
+[dbk:article] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:section (dbk:section) = dbk:section *
+ - dbk:class (String) 
+
+[dbk:audiodata] > argeodbk:base
+ + dbk:info (dbk:info) = dbk:info
+ - dbk:entityref (String) 
+ - dbk:fileref (String) 
+ - dbk:format (String) 
+
+[dbk:audioobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:audiodata (dbk:audiodata) = dbk:audiodata
+ + dbk:info (dbk:info) = dbk:info
+
+[dbk:author] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
+ + dbk:orgname (dbk:orgname) = dbk:orgname
+ + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
+ + dbk:personname (dbk:personname) = dbk:personname
+
+[dbk:biblioref] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:begin (String) 
+ - dbk:end (String) 
+ - dbk:endterm (Reference) 
+ - dbk:units (String) 
+ - dbk:xrefstyle (String) 
+
+[dbk:book] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:article (dbk:article) = dbk:article *
+ + dbk:chapter (dbk:chapter) = dbk:chapter *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:caption] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+ - dbk:class (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:style (String) 
+ - dbk:title (String) 
+
+[dbk:chapter] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:section (dbk:section) = dbk:section *
+
+[dbk:colspec] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:colname (String) 
+ - dbk:colnum (String) 
+ - dbk:colsep (String) 
+ - dbk:colwidth (String) 
+ - dbk:rowsep (String) 
+
+[dbk:editor] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
+ + dbk:orgname (dbk:orgname) = dbk:orgname
+ + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
+ + dbk:personname (dbk:personname) = dbk:personname
+
+[dbk:emphasis] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:entry] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:colname (String) 
+ - dbk:colsep (String) 
+ - dbk:morerows (String) 
+ - dbk:nameend (String) 
+ - dbk:namest (String) 
+ - dbk:rotate (String) 
+ - dbk:rowsep (String) 
+ - dbk:spanname (String) 
+ - dbk:valign (String) 
+
+[dbk:entrytbl] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:colspec (dbk:colspec) = dbk:colspec *
+ + dbk:spanspec (dbk:spanspec) = dbk:spanspec *
+ + dbk:tbody (dbk:tbody) = dbk:tbody
+ + dbk:thead (dbk:thead) = dbk:thead
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:colname (String) 
+ - dbk:cols (String) 
+ - dbk:colsep (String) 
+ - dbk:nameend (String) 
+ - dbk:namest (String) 
+ - dbk:rowsep (String) 
+ - dbk:spanname (String) 
+ - dbk:tgroupstyle (String) 
+
+[dbk:imagedata] > argeodbk:base
+ + dbk:info (dbk:info) = dbk:info
+ - dbk:align (String) 
+ - dbk:contentdepth (String) 
+ - dbk:contentwidth (String) 
+ - dbk:depth (String) 
+ - dbk:entityref (String) 
+ - dbk:fileref (String) 
+ - dbk:format (String) 
+ - dbk:scale (String) 
+ - dbk:scalefit (String) 
+ - dbk:valign (String) 
+ - dbk:width (String) 
+
+[dbk:imageobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:imagedata (dbk:imagedata) = dbk:imagedata
+ + dbk:info (dbk:info) = dbk:info
+
+[dbk:info] > argeodbk:base
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:author (dbk:author) = dbk:author *
+ + dbk:editor (dbk:editor) = dbk:editor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:orgname (dbk:orgname) = dbk:orgname *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ + dbk:title (dbk:title) = dbk:title *
+
+[dbk:inlinemediaobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:alt (dbk:alt) = dbk:alt
+ + dbk:audioobject (dbk:audioobject) = dbk:audioobject *
+ + dbk:imageobject (dbk:imageobject) = dbk:imageobject *
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ + dbk:videoobject (dbk:videoobject) = dbk:videoobject *
+
+[dbk:itemizedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:listitem (dbk:listitem) = dbk:listitem *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ - dbk:mark (String) 
+ - dbk:spacing (String) 
+
+[dbk:link] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:endterm (Reference) 
+ - dbk:xrefstyle (String) 
+
+[dbk:listitem] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ - dbk:override (String) 
+
+[dbk:mediaobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:alt (dbk:alt) = dbk:alt
+ + dbk:audioobject (dbk:audioobject) = dbk:audioobject *
+ + dbk:caption (dbk:caption) = dbk:caption
+ + dbk:imageobject (dbk:imageobject) = dbk:imageobject *
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ + dbk:videoobject (dbk:videoobject) = dbk:videoobject *
+
+[dbk:olink] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:localinfo (String) 
+ - dbk:targetdoc (String) 
+ - dbk:targetptr (String) 
+ - dbk:type (String) 
+ - dbk:xrefstyle (String) 
+
+[dbk:orderedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:listitem (dbk:listitem) = dbk:listitem *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ - dbk:continuation (String) 
+ - dbk:inheritnum (String) 
+ - dbk:numeration (String) 
+ - dbk:spacing (String) 
+ - dbk:startingnumber (String) 
+
+[dbk:orgdiv] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:orgname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+ - dbk:otherclass (String) 
+
+[dbk:para] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:info (dbk:info) = dbk:info *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+
+[dbk:personblurb] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+
+[dbk:personname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:phrase] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:remark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:replaceable] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String) 
+
+[dbk:row] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:entry (dbk:entry) = dbk:entry *
+ + dbk:entrytbl (dbk:entrytbl) = dbk:entrytbl *
+ - dbk:rowsep (String) 
+ - dbk:valign (String) 
+
+[dbk:section] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:section (dbk:section) = dbk:section *
+
+[dbk:set] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:book (dbk:book) = dbk:book *
+ + dbk:set (dbk:set) = dbk:set *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String) 
+ - dbk:status (String) 
+
+[dbk:simplelist] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:columns (String) 
+ - dbk:type (String) 
+
+[dbk:spanspec] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:colsep (String) 
+ - dbk:nameend (String) 
+ - dbk:namest (String) 
+ - dbk:rowsep (String) 
+ - dbk:spanname (String) 
+
+[dbk:subscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:subtitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:superscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:table] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:caption (dbk:caption) = dbk:caption
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:tbody (dbk:tbody) = dbk:tbody *
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ + dbk:tfoot (dbk:tfoot) = dbk:tfoot
+ + dbk:tgroup (dbk:tgroup) = dbk:tgroup *
+ + dbk:thead (dbk:thead) = dbk:thead
+ - dbk:border (String) 
+ - dbk:cellpadding (String) 
+ - dbk:cellspacing (String) 
+ - dbk:class (String) 
+ - dbk:colsep (String) 
+ - dbk:floatstyle (String) 
+ - dbk:frame (String) 
+ - dbk:label (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:orient (String) 
+ - dbk:pgwide (String) 
+ - dbk:rowheader (String) 
+ - dbk:rowsep (String) 
+ - dbk:rules (String) 
+ - dbk:shortentry (String) 
+ - dbk:style (String) 
+ - dbk:summary (String) 
+ - dbk:tabstyle (String) 
+ - dbk:title (String) 
+ - dbk:tocentry (String) 
+ - dbk:width (String) 
+
+[dbk:tbody] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:row (dbk:row) = dbk:row *
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:class (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:style (String) 
+ - dbk:title (String) 
+ - dbk:valign (String) 
+
+[dbk:textobject] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:phrase (dbk:phrase) = dbk:phrase
+ + dbk:remark (dbk:remark) = dbk:remark *
+
+[dbk:tfoot] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:colspec (dbk:colspec) = dbk:colspec *
+ + dbk:row (dbk:row) = dbk:row *
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:class (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:style (String) 
+ - dbk:title (String) 
+ - dbk:valign (String) 
+
+[dbk:tgroup] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:colspec (dbk:colspec) = dbk:colspec *
+ + dbk:spanspec (dbk:spanspec) = dbk:spanspec *
+ + dbk:tbody (dbk:tbody) = dbk:tbody
+ + dbk:tfoot (dbk:tfoot) = dbk:tfoot
+ + dbk:thead (dbk:thead) = dbk:thead
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:cols (String) 
+ - dbk:colsep (String) 
+ - dbk:rowsep (String) 
+ - dbk:tgroupstyle (String) 
+
+[dbk:thead] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:colspec (dbk:colspec) = dbk:colspec *
+ + dbk:row (dbk:row) = dbk:row *
+ - dbk:align (String) 
+ - dbk:char (String) 
+ - dbk:charoff (String) 
+ - dbk:class (String) 
+ - dbk:lang (String) 
+ - dbk:onclick (String) 
+ - dbk:ondblclick (String) 
+ - dbk:onkeydown (String) 
+ - dbk:onkeypress (String) 
+ - dbk:onkeyup (String) 
+ - dbk:onmousedown (String) 
+ - dbk:onmousemove (String) 
+ - dbk:onmouseout (String) 
+ - dbk:onmouseover (String) 
+ - dbk:onmouseup (String) 
+ - dbk:style (String) 
+ - dbk:title (String) 
+ - dbk:valign (String) 
+
+[dbk:title] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:videodata] > argeodbk:base
+ + dbk:info (dbk:info) = dbk:info
+ - dbk:align (String) 
+ - dbk:contentdepth (String) 
+ - dbk:contentwidth (String) 
+ - dbk:depth (String) 
+ - dbk:entityref (String) 
+ - dbk:fileref (String) 
+ - dbk:format (String) 
+ - dbk:scale (String) 
+ - dbk:scalefit (String) 
+ - dbk:valign (String) 
+ - dbk:width (String) 
+
+[dbk:videoobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:videodata (dbk:videodata) = dbk:videodata
+
+[dbk:xmltext] > nt:base
+ - jcr:xmlcharacters (String) 
+
+[dbk:xref] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:endterm (Reference) 
+ - dbk:xrefstyle (String) 
+
+
diff --git a/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileStore.java b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileStore.java
new file mode 100644 (file)
index 0000000..32a3ecb
--- /dev/null
@@ -0,0 +1,72 @@
+package org.argeo.jcr.fs;
+
+import java.io.IOException;
+import java.nio.file.FileStore;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.attribute.FileStoreAttributeView;
+
+public class JcrFileStore extends FileStore {
+
+       @Override
+       public String name() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public String type() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public boolean isReadOnly() {
+               // TODO Auto-generated method stub
+               return false;
+       }
+
+       @Override
+       public long getTotalSpace() throws IOException {
+               // TODO Auto-generated method stub
+               return 0;
+       }
+
+       @Override
+       public long getUsableSpace() throws IOException {
+               // TODO Auto-generated method stub
+               return 0;
+       }
+
+       @Override
+       public long getUnallocatedSpace() throws IOException {
+               // TODO Auto-generated method stub
+               return 0;
+       }
+
+       @Override
+       public boolean supportsFileAttributeView(
+                       Class<? extends FileAttributeView> type) {
+               // TODO Auto-generated method stub
+               return false;
+       }
+
+       @Override
+       public boolean supportsFileAttributeView(String name) {
+               // TODO Auto-generated method stub
+               return false;
+       }
+
+       @Override
+       public <V extends FileStoreAttributeView> V getFileStoreAttributeView(
+                       Class<V> type) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Object getAttribute(String attribute) throws IOException {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystem.java b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystem.java
new file mode 100644 (file)
index 0000000..40328e8
--- /dev/null
@@ -0,0 +1,97 @@
+package org.argeo.jcr.fs;
+
+import java.io.IOException;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.WatchService;
+import java.nio.file.attribute.UserPrincipalLookupService;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.Set;
+
+import javax.jcr.Session;
+
+import org.argeo.jcr.JcrUtils;
+
+public class JcrFileSystem extends FileSystem {
+       private final JcrFileSystemProvider provider;
+       private final Session session;
+
+       public JcrFileSystem(JcrFileSystemProvider provider, Session session) {
+               super();
+               this.provider = provider;
+               this.session = session;
+       }
+
+       @Override
+       public FileSystemProvider provider() {
+               return provider;
+       }
+
+       @Override
+       public void close() throws IOException {
+               JcrUtils.logoutQuietly(session);
+       }
+
+       @Override
+       public boolean isOpen() {
+               return session.isLive();
+       }
+
+       @Override
+       public boolean isReadOnly() {
+               return false;
+       }
+
+       @Override
+       public String getSeparator() {
+               return "/";
+       }
+
+       @Override
+       public Iterable<Path> getRootDirectories() {
+               return null;
+       }
+
+       @Override
+       public Iterable<FileStore> getFileStores() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Set<String> supportedFileAttributeViews() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Path getPath(String first, String... more) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public PathMatcher getPathMatcher(String syntaxAndPattern) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public UserPrincipalLookupService getUserPrincipalLookupService() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public WatchService newWatchService() throws IOException {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       public Session getSession() {
+               return session;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java
new file mode 100644 (file)
index 0000000..8ea4cca
--- /dev/null
@@ -0,0 +1,142 @@
+package org.argeo.jcr.fs;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.AccessMode;
+import java.nio.file.CopyOption;
+import java.nio.file.DirectoryStream;
+import java.nio.file.DirectoryStream.Filter;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.Map;
+import java.util.Set;
+
+public class JcrFileSystemProvider extends FileSystemProvider {
+
+       @Override
+       public String getScheme() {
+               return "jcr";
+       }
+
+       @Override
+       public FileSystem newFileSystem(URI uri, Map<String, ?> env)
+                       throws IOException {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public FileSystem getFileSystem(URI uri) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Path getPath(URI uri) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public SeekableByteChannel newByteChannel(Path path,
+                       Set<? extends OpenOption> options, FileAttribute<?>... attrs)
+                       throws IOException {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public DirectoryStream<Path> newDirectoryStream(Path dir,
+                       Filter<? super Path> filter) throws IOException {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public void createDirectory(Path dir, FileAttribute<?>... attrs)
+                       throws IOException {
+               // TODO Auto-generated method stub
+
+       }
+
+       @Override
+       public void delete(Path path) throws IOException {
+               // TODO Auto-generated method stub
+
+       }
+
+       @Override
+       public void copy(Path source, Path target, CopyOption... options)
+                       throws IOException {
+               // TODO Auto-generated method stub
+
+       }
+
+       @Override
+       public void move(Path source, Path target, CopyOption... options)
+                       throws IOException {
+               // TODO Auto-generated method stub
+
+       }
+
+       @Override
+       public boolean isSameFile(Path path, Path path2) throws IOException {
+               // TODO Auto-generated method stub
+               return false;
+       }
+
+       @Override
+       public boolean isHidden(Path path) throws IOException {
+               // TODO Auto-generated method stub
+               return false;
+       }
+
+       @Override
+       public FileStore getFileStore(Path path) throws IOException {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public void checkAccess(Path path, AccessMode... modes) throws IOException {
+               // TODO Auto-generated method stub
+
+       }
+
+       @Override
+       public <V extends FileAttributeView> V getFileAttributeView(Path path,
+                       Class<V> type, LinkOption... options) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public <A extends BasicFileAttributes> A readAttributes(Path path,
+                       Class<A> type, LinkOption... options) throws IOException {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Map<String, Object> readAttributes(Path path, String attributes,
+                       LinkOption... options) throws IOException {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public void setAttribute(Path path, String attribute, Object value,
+                       LinkOption... options) throws IOException {
+               // TODO Auto-generated method stub
+
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFsException.java b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFsException.java
new file mode 100644 (file)
index 0000000..f214fdc
--- /dev/null
@@ -0,0 +1,15 @@
+package org.argeo.jcr.fs;
+
+
+/** Exception related to the JCR FS */
+public class JcrFsException extends RuntimeException {
+       private static final long serialVersionUID = -7973896038244922980L;
+
+       public JcrFsException(String message, Throwable e) {
+               super(message, e);
+       }
+
+       public JcrFsException(String message) {
+               super(message);
+       }
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/fs/JcrPath.java b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrPath.java
new file mode 100644 (file)
index 0000000..e252935
--- /dev/null
@@ -0,0 +1,199 @@
+package org.argeo.jcr.fs;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.FileSystem;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.WatchEvent.Kind;
+import java.nio.file.WatchEvent.Modifier;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.Iterator;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+public class JcrPath implements Path {
+       private JcrFileSystem filesSystem;
+       private String path;
+
+       private Node node;
+
+       public JcrPath(JcrFileSystem filesSystem, Node node) {
+               super();
+               this.filesSystem = filesSystem;
+               this.node = node;
+       }
+
+       @Override
+       public FileSystem getFileSystem() {
+               return filesSystem;
+       }
+
+       @Override
+       public boolean isAbsolute() {
+               return path.startsWith("/");
+       }
+
+       @Override
+       public Path getRoot() {
+               try {
+                       return new JcrPath(filesSystem, node.getSession().getRootNode());
+               } catch (RepositoryException e) {
+                       throw new JcrFsException("Cannot get root", e);
+               }
+       }
+
+       @Override
+       public Path getFileName() {
+               return null;
+       }
+
+       @Override
+       public Path getParent() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public int getNameCount() {
+               // TODO Auto-generated method stub
+               return 0;
+       }
+
+       @Override
+       public Path getName(int index) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Path subpath(int beginIndex, int endIndex) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public boolean startsWith(Path other) {
+               // TODO Auto-generated method stub
+               return false;
+       }
+
+       @Override
+       public boolean startsWith(String other) {
+               // TODO Auto-generated method stub
+               return false;
+       }
+
+       @Override
+       public boolean endsWith(Path other) {
+               // TODO Auto-generated method stub
+               return false;
+       }
+
+       @Override
+       public boolean endsWith(String other) {
+               // TODO Auto-generated method stub
+               return false;
+       }
+
+       @Override
+       public Path normalize() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Path resolve(Path other) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Path resolve(String other) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Path resolveSibling(Path other) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Path resolveSibling(String other) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Path relativize(Path other) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public URI toUri() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Path toAbsolutePath() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Path toRealPath(LinkOption... options) throws IOException {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public File toFile() {
+               throw new UnsupportedOperationException();
+       }
+
+       @Override
+       public WatchKey register(WatchService watcher, Kind<?>[] events,
+                       Modifier... modifiers) throws IOException {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public WatchKey register(WatchService watcher, Kind<?>... events)
+                       throws IOException {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public Iterator<Path> iterator() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public int compareTo(Path other) {
+               // TODO Auto-generated method stub
+               return 0;
+       }
+
+       public Node getNode() {
+               if (!isAbsolute())// TODO default dir
+                       throw new JcrFsException("Cannot get node from relative path");
+               try {
+                       if (node == null)
+                               node = filesSystem.getSession().getNode(path);
+                       return node;
+               } catch (RepositoryException e) {
+                       throw new JcrFsException("Cannot get node", e);
+               }
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/proxy/AbstractUrlProxy.java b/org.argeo.jcr/src/org/argeo/jcr/proxy/AbstractUrlProxy.java
new file mode 100644 (file)
index 0000000..30369ce
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr.proxy;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.jcr.ArgeoJcrException;
+import org.argeo.jcr.JcrUtils;
+
+/** Base class for URL based proxys. */
+public abstract class AbstractUrlProxy implements ResourceProxy {
+       private final static Log log = LogFactory.getLog(AbstractUrlProxy.class);
+
+       private Repository jcrRepository;
+       private Session jcrAdminSession;
+       private String proxyWorkspace = "proxy";
+
+       protected abstract Node retrieve(Session session, String path);
+
+       void init() {
+               try {
+                       jcrAdminSession = JcrUtils.loginOrCreateWorkspace(jcrRepository,
+                                       proxyWorkspace);
+                       beforeInitSessionSave(jcrAdminSession);
+                       if (jcrAdminSession.hasPendingChanges())
+                               jcrAdminSession.save();
+               } catch (Exception e) {
+                       JcrUtils.discardQuietly(jcrAdminSession);
+                       throw new ArgeoJcrException("Cannot initialize Maven proxy", e);
+               }
+       }
+
+       /**
+        * Called before the (admin) session is saved at the end of the
+        * initialization. Does nothing by default, to be overridden.
+        */
+       protected void beforeInitSessionSave(Session session)
+                       throws RepositoryException {
+       }
+
+       void destroy() {
+               JcrUtils.logoutQuietly(jcrAdminSession);
+       }
+
+       /**
+        * Called before the (admin) session is logged out when resources are
+        * released. Does nothing by default, to be overridden.
+        */
+       protected void beforeDestroySessionLogout() throws RepositoryException {
+       }
+
+       public Node proxy(String path) {
+               // we open a JCR session with client credentials in order not to use the
+               // admin session in multiple thread or make it a bottleneck.
+               Node nodeAdmin = null;
+               Node nodeClient = null;
+               Session clientSession = null;
+               try {
+                       clientSession = jcrRepository.login(proxyWorkspace);
+                       if (!clientSession.itemExists(path)
+                                       || shouldUpdate(clientSession, path)) {
+                               nodeAdmin = retrieveAndSave(path);
+                               if (nodeAdmin != null)
+                                       nodeClient = clientSession.getNode(path);
+                       } else
+                               nodeClient = clientSession.getNode(path);
+                       return nodeClient;
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot proxy " + path, e);
+               } finally {
+                       if (nodeClient == null)
+                               JcrUtils.logoutQuietly(clientSession);
+               }
+       }
+
+       protected synchronized Node retrieveAndSave(String path) {
+               try {
+                       Node node = retrieve(jcrAdminSession, path);
+                       if (node == null)
+                               return null;
+                       jcrAdminSession.save();
+                       return node;
+               } catch (RepositoryException e) {
+                       JcrUtils.discardQuietly(jcrAdminSession);
+                       throw new ArgeoJcrException("Cannot retrieve and save " + path, e);
+               } finally {
+                       notifyAll();
+               }
+       }
+
+       /** Session is not saved */
+       protected synchronized Node proxyUrl(Session session, String remoteUrl,
+                       String path) throws RepositoryException {
+               Node node = null;
+               if (session.itemExists(path)) {
+                       // throw new ArgeoJcrException("Node " + path + " already exists");
+               }
+               InputStream in = null;
+               try {
+                       URL u = new URL(remoteUrl);
+                       in = u.openStream();
+                       node = importFile(session, path, in);
+               } catch (IOException e) {
+                       if (log.isDebugEnabled()) {
+                               log.debug("Cannot read " + remoteUrl + ", skipping... "
+                                               + e.getMessage());
+                               // log.trace("Cannot read because of ", e);
+                       }
+                       JcrUtils.discardQuietly(session);
+               } finally {
+                       IOUtils.closeQuietly(in);
+               }
+               return node;
+       }
+
+       protected synchronized Node importFile(Session session, String path,
+                       InputStream in) throws RepositoryException {
+               Binary binary = null;
+               try {
+                       Node content = null;
+                       Node node = null;
+                       if (!session.itemExists(path)) {
+                               node = JcrUtils.mkdirs(session, path, NodeType.NT_FILE,
+                                               NodeType.NT_FOLDER, false);
+                               content = node.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
+                       } else {
+                               node = session.getNode(path);
+                               content = node.getNode(Node.JCR_CONTENT);
+                       }
+                       binary = session.getValueFactory().createBinary(in);
+                       content.setProperty(Property.JCR_DATA, binary);
+                       JcrUtils.updateLastModifiedAndParents(node, null);
+                       return node;
+               } finally {
+                       JcrUtils.closeQuietly(binary);
+               }
+       }
+
+       /** Whether the file should be updated. */
+       protected Boolean shouldUpdate(Session clientSession, String nodePath) {
+               return false;
+       }
+
+       public void setJcrRepository(Repository jcrRepository) {
+               this.jcrRepository = jcrRepository;
+       }
+
+       public void setProxyWorkspace(String localWorkspace) {
+               this.proxyWorkspace = localWorkspace;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/proxy/ResourceProxy.java b/org.argeo.jcr/src/org/argeo/jcr/proxy/ResourceProxy.java
new file mode 100644 (file)
index 0000000..b4fb332
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr.proxy;
+
+import javax.jcr.Node;
+
+/** A proxy which nows how to resolve and synchronize relative URLs */
+public interface ResourceProxy {
+       /**
+        * Proxy the file referenced by this relative path in the underlying
+        * repository. A new session is created by each call, so the underlying
+        * session of the returned node must be closed by the caller.
+        * 
+        * @return the proxied Node, <code>null</code> if the resource was not found
+        *         (e.g. HTTP 404)
+        */
+       public Node proxy(String relativePath);
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/proxy/ResourceProxyServlet.java b/org.argeo.jcr/src/org/argeo/jcr/proxy/ResourceProxyServlet.java
new file mode 100644 (file)
index 0000000..e92e2a4
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr.proxy;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.jcr.ArgeoJcrException;
+import org.argeo.jcr.JcrUtils;
+
+/** Wraps a proxy via HTTP */
+public class ResourceProxyServlet extends HttpServlet {
+       private static final long serialVersionUID = -8886549549223155801L;
+
+       private final static Log log = LogFactory
+                       .getLog(ResourceProxyServlet.class);
+
+       private ResourceProxy proxy;
+
+       private String contentTypeCharset = "UTF-8";
+
+       @Override
+       protected void doGet(HttpServletRequest request,
+                       HttpServletResponse response) throws ServletException, IOException {
+               String path = request.getPathInfo();
+
+               if (log.isTraceEnabled()) {
+                       log.trace("path=" + path);
+                       log.trace("UserPrincipal = " + request.getUserPrincipal().getName());
+                       log.trace("SessionID = " + request.getSession().getId());
+                       log.trace("ContextPath = " + request.getContextPath());
+                       log.trace("ServletPath = " + request.getServletPath());
+                       log.trace("PathInfo = " + request.getPathInfo());
+                       log.trace("Method = " + request.getMethod());
+                       log.trace("User-Agent = " + request.getHeader("User-Agent"));
+               }
+
+               Node node = null;
+               try {
+                       node = proxy.proxy(path);
+                       if (node == null)
+                               response.sendError(404);
+                       else
+                               processResponse(node, response);
+               } finally {
+                       if (node != null)
+                               try {
+                                       JcrUtils.logoutQuietly(node.getSession());
+                               } catch (RepositoryException e) {
+                                       // silent
+                               }
+               }
+
+       }
+
+       /** Retrieve the content of the node. */
+       protected void processResponse(Node node, HttpServletResponse response) {
+               Binary binary = null;
+               InputStream in = null;
+               try {
+                       String fileName = node.getName();
+                       String ext = FilenameUtils.getExtension(fileName);
+
+                       // TODO use a more generic / standard approach
+                       // see http://svn.apache.org/viewvc/tomcat/trunk/conf/web.xml
+                       String contentType;
+                       if ("xml".equals(ext))
+                               contentType = "text/xml;charset=" + contentTypeCharset;
+                       else if ("jar".equals(ext))
+                               contentType = "application/java-archive";
+                       else if ("zip".equals(ext))
+                               contentType = "application/zip";
+                       else if ("gz".equals(ext))
+                               contentType = "application/x-gzip";
+                       else if ("bz2".equals(ext))
+                               contentType = "application/x-bzip2";
+                       else if ("tar".equals(ext))
+                               contentType = "application/x-tar";
+                       else if ("rpm".equals(ext))
+                               contentType = "application/x-redhat-package-manager";
+                       else
+                               contentType = "application/octet-stream";
+                       contentType = contentType + ";name=\"" + fileName + "\"";
+                       response.setHeader("Content-Disposition", "attachment; filename=\""
+                                       + fileName + "\"");
+                       response.setHeader("Expires", "0");
+                       response.setHeader("Cache-Control", "no-cache, must-revalidate");
+                       response.setHeader("Pragma", "no-cache");
+
+                       response.setContentType(contentType);
+
+                       try {
+                               binary = node.getNode(Property.JCR_CONTENT)
+                                               .getProperty(Property.JCR_DATA).getBinary();
+                       } catch (PathNotFoundException e) {
+                               log.error("Node " + node + " as no data under content");
+                               throw e;
+                       }
+                       in = binary.getStream();
+                       IOUtils.copy(in, response.getOutputStream());
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot download " + node, e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+                       JcrUtils.closeQuietly(binary);
+               }
+       }
+
+       public void setProxy(ResourceProxy resourceProxy) {
+               this.proxy = resourceProxy;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/security/JcrAuthorizations.java b/org.argeo.jcr/src/org/argeo/jcr/security/JcrAuthorizations.java
new file mode 100644 (file)
index 0000000..9ad2492
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr.security;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.Privilege;
+
+import org.argeo.jcr.ArgeoJcrException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.util.security.SimplePrincipal;
+
+/** Apply authorizations to a JCR repository. */
+public class JcrAuthorizations implements Runnable {
+       // private final static Log log =
+       // LogFactory.getLog(JcrAuthorizations.class);
+
+       private Repository repository;
+       private String workspace = null;
+
+       private String securityWorkspace = "security";
+
+       /**
+        * key := privilege1,privilege2/path/to/node<br/>
+        * value := group1,group2,user1
+        */
+       private Map<String, String> principalPrivileges = new HashMap<String, String>();
+
+       public void run() {
+               String currentWorkspace = workspace;
+               Session session = null;
+               try {
+                       if (workspace != null && workspace.equals("*")) {
+                               session = repository.login();
+                               String[] workspaces = session.getWorkspace()
+                                               .getAccessibleWorkspaceNames();
+                               JcrUtils.logoutQuietly(session);
+                               for (String wksp : workspaces) {
+                                       currentWorkspace = wksp;
+                                       if (currentWorkspace.equals(securityWorkspace))
+                                               continue;
+                                       session = repository.login(currentWorkspace);
+                                       initAuthorizations(session);
+                                       JcrUtils.logoutQuietly(session);
+                               }
+                       } else {
+                               session = repository.login(workspace);
+                               initAuthorizations(session);
+                       }
+               } catch (Exception e) {
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoJcrException(
+                                       "Cannot set authorizations " + principalPrivileges
+                                                       + " on workspace " + currentWorkspace, e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+               }
+       }
+
+       protected void processWorkspace(String workspace) {
+               Session session = null;
+               try {
+                       session = repository.login(workspace);
+                       initAuthorizations(session);
+               } catch (Exception e) {
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoJcrException("Cannot set authorizations "
+                                       + principalPrivileges + " on repository " + repository, e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+               }
+       }
+
+       /** @deprecated call {@link #run()} instead. */
+       @Deprecated
+       public void init() {
+               run();
+       }
+
+       protected void initAuthorizations(Session session)
+                       throws RepositoryException {
+               AccessControlManager acm = session.getAccessControlManager();
+
+               for (String privileges : principalPrivileges.keySet()) {
+                       String path = null;
+                       int slashIndex = privileges.indexOf('/');
+                       if (slashIndex == 0) {
+                               throw new ArgeoJcrException("Privilege " + privileges
+                                               + " badly formatted it starts with /");
+                       } else if (slashIndex > 0) {
+                               path = privileges.substring(slashIndex);
+                               privileges = privileges.substring(0, slashIndex);
+                       }
+
+                       if (path == null)
+                               path = "/";
+
+                       List<Privilege> privs = new ArrayList<Privilege>();
+                       for (String priv : privileges.split(",")) {
+                               privs.add(acm.privilegeFromName(priv));
+                       }
+
+                       String principalNames = principalPrivileges.get(privileges);
+                       for (String principalName : principalNames.split(",")) {
+                               Principal principal = getOrCreatePrincipal(session,
+                                               principalName);
+                               JcrUtils.addPrivileges(session, path, principal, privs);
+                               // if (log.isDebugEnabled()) {
+                               // StringBuffer privBuf = new StringBuffer();
+                               // for (Privilege priv : privs)
+                               // privBuf.append(priv.getName());
+                               // log.debug("Added privileges " + privBuf + " to "
+                               // + principal.getName() + " on " + path + " in '"
+                               // + session.getWorkspace().getName() + "'");
+                               // }
+                       }
+               }
+
+               // if (log.isDebugEnabled())
+               // log.debug("JCR authorizations applied on '"
+               // + session.getWorkspace().getName() + "'");
+       }
+
+       /**
+        * Returns a {@link SimplePrincipal}, does not check whether it exists since
+        * such capabilities is not provided by the standard JCR API. Can be
+        * overridden to provide smarter handling
+        */
+       protected Principal getOrCreatePrincipal(Session session,
+                       String principalName) throws RepositoryException {
+               return new SimplePrincipal(principalName);
+       }
+
+       // public static void addPrivileges(Session session, Principal principal,
+       // String path, List<Privilege> privs) throws RepositoryException {
+       // AccessControlManager acm = session.getAccessControlManager();
+       // // search for an access control list
+       // AccessControlList acl = null;
+       // AccessControlPolicyIterator policyIterator = acm
+       // .getApplicablePolicies(path);
+       // if (policyIterator.hasNext()) {
+       // while (policyIterator.hasNext()) {
+       // AccessControlPolicy acp = policyIterator
+       // .nextAccessControlPolicy();
+       // if (acp instanceof AccessControlList)
+       // acl = ((AccessControlList) acp);
+       // }
+       // } else {
+       // AccessControlPolicy[] existingPolicies = acm.getPolicies(path);
+       // for (AccessControlPolicy acp : existingPolicies) {
+       // if (acp instanceof AccessControlList)
+       // acl = ((AccessControlList) acp);
+       // }
+       // }
+       //
+       // if (acl != null) {
+       // acl.addAccessControlEntry(principal,
+       // privs.toArray(new Privilege[privs.size()]));
+       // acm.setPolicy(path, acl);
+       // session.save();
+       // if (log.isDebugEnabled()) {
+       // StringBuffer buf = new StringBuffer("");
+       // for (int i = 0; i < privs.size(); i++) {
+       // if (i != 0)
+       // buf.append(',');
+       // buf.append(privs.get(i).getName());
+       // }
+       // log.debug("Added privilege(s) '" + buf + "' to '"
+       // + principal.getName() + "' on " + path
+       // + " from workspace '"
+       // + session.getWorkspace().getName() + "'");
+       // }
+       // } else {
+       // throw new ArgeoJcrException("Don't know how to apply  privileges "
+       // + privs + " to " + principal + " on " + path
+       // + " from workspace '" + session.getWorkspace().getName()
+       // + "'");
+       // }
+       // }
+
+       @Deprecated
+       public void setGroupPrivileges(Map<String, String> groupPrivileges) {
+               this.principalPrivileges = groupPrivileges;
+       }
+
+       public void setPrincipalPrivileges(Map<String, String> principalPrivileges) {
+               this.principalPrivileges = principalPrivileges;
+       }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setWorkspace(String workspace) {
+               this.workspace = workspace;
+       }
+
+       public void setSecurityWorkspace(String securityWorkspace) {
+               this.securityWorkspace = securityWorkspace;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/security/JcrKeyring.java b/org.argeo.jcr/src/org/argeo/jcr/security/JcrKeyring.java
new file mode 100644 (file)
index 0000000..8ab6ed3
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr.security;
+
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayReader;
+import java.io.InputStream;
+import java.io.Reader;
+import java.security.Provider;
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.jcr.ArgeoJcrException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.util.security.AbstractKeyring;
+import org.argeo.util.security.PBEKeySpecCallback;
+
+/** JCR based implementation of a keyring */
+public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
+       /**
+        * Stronger with 256, but causes problem with Oracle JVM, force 128 in this
+        * case
+        */
+       public final static Long DEFAULT_SECRETE_KEY_LENGTH = 256l;
+       public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
+       public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
+       public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding";
+
+       private Integer iterationCountFactor = 200;
+       private Long secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
+       private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
+       private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
+       private String cipherName = DEFAULT_CIPHER_NAME;
+
+       private Session session;
+
+       /**
+        * When setup is called the session has not yet been saved and we don't want
+        * to save it since there maybe other data which would be inconsistent. So
+        * we keep a reference to this node which will then be used (an reset to
+        * null) when handling the PBE callback. We keep one per thread in case
+        * multiple users are accessing the same instance of a keyring.
+        */
+       private ThreadLocal<Node> notYetSavedKeyring = new ThreadLocal<Node>() {
+
+               @Override
+               protected Node initialValue() {
+                       return null;
+               }
+       };
+
+       @Override
+       protected Boolean isSetup() {
+               try {
+                       if (notYetSavedKeyring.get() != null)
+                               return true;
+
+                       Node userHome = UserJcrUtils.getUserHome(session);
+                       return userHome.hasNode(ARGEO_KEYRING);
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot check whether keyring is setup", e);
+               }
+       }
+
+       @Override
+       protected void setup(char[] password) {
+               Binary binary = null;
+               InputStream in = null;
+               try {
+                       Node userHome = UserJcrUtils.getUserHome(session);
+                       if (userHome.hasNode(ARGEO_KEYRING))
+                               throw new ArgeoJcrException("Keyring already setup");
+                       Node keyring = userHome.addNode(ARGEO_KEYRING);
+                       keyring.addMixin(ArgeoTypes.ARGEO_PBE_SPEC);
+
+                       // deterministic salt and iteration count based on username
+                       String username = session.getUserID();
+                       byte[] salt = new byte[8];
+                       byte[] usernameBytes = username.getBytes();
+                       for (int i = 0; i < salt.length; i++) {
+                               if (i < usernameBytes.length)
+                                       salt[i] = usernameBytes[i];
+                               else
+                                       salt[i] = 0;
+                       }
+                       in = new ByteArrayInputStream(salt);
+                       binary = session.getValueFactory().createBinary(in);
+                       keyring.setProperty(ARGEO_SALT, binary);
+
+                       Integer iterationCount = username.length() * iterationCountFactor;
+                       keyring.setProperty(ARGEO_ITERATION_COUNT, iterationCount);
+
+                       // default algo
+                       // TODO check if algo and key length are available, use DES if not
+                       keyring.setProperty(ARGEO_SECRET_KEY_FACTORY, secreteKeyFactoryName);
+                       keyring.setProperty(ARGEO_KEY_LENGTH, secreteKeyLength);
+                       keyring.setProperty(ARGEO_SECRET_KEY_ENCRYPTION, secreteKeyEncryption);
+                       keyring.setProperty(ARGEO_CIPHER, cipherName);
+
+                       // keyring.getSession().save();
+
+                       // encrypted password hash
+                       // IOUtils.closeQuietly(in);
+                       // JcrUtils.closeQuietly(binary);
+                       // byte[] btPass = hash(password, salt, iterationCount);
+                       // in = new ByteArrayInputStream(btPass);
+                       // binary = session.getValueFactory().createBinary(in);
+                       // keyring.setProperty(ARGEO_PASSWORD, binary);
+
+                       notYetSavedKeyring.set(keyring);
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot setup keyring", e);
+               } finally {
+                       JcrUtils.closeQuietly(binary);
+                       IOUtils.closeQuietly(in);
+                       // JcrUtils.discardQuietly(session);
+               }
+       }
+
+       @Override
+       protected void handleKeySpecCallback(PBEKeySpecCallback pbeCallback) {
+               try {
+                       Node userHome = UserJcrUtils.getUserHome(session);
+                       Node keyring;
+                       if (userHome.hasNode(ARGEO_KEYRING))
+                               keyring = userHome.getNode(ARGEO_KEYRING);
+                       else if (notYetSavedKeyring.get() != null)
+                               keyring = notYetSavedKeyring.get();
+                       else
+                               throw new ArgeoJcrException("Keyring not setup");
+
+                       pbeCallback.set(keyring.getProperty(ARGEO_SECRET_KEY_FACTORY).getString(),
+                                       JcrUtils.getBinaryAsBytes(keyring.getProperty(ARGEO_SALT)),
+                                       (int) keyring.getProperty(ARGEO_ITERATION_COUNT).getLong(),
+                                       (int) keyring.getProperty(ARGEO_KEY_LENGTH).getLong(),
+                                       keyring.getProperty(ARGEO_SECRET_KEY_ENCRYPTION).getString());
+
+                       if (notYetSavedKeyring.get() != null)
+                               notYetSavedKeyring.remove();
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot handle key spec callback", e);
+               }
+       }
+
+       /** The parent node must already exist at this path. */
+       @Override
+       protected synchronized void encrypt(String path, InputStream unencrypted) {
+               // should be called first for lazy initialization
+               SecretKey secretKey = getSecretKey();
+
+               Binary binary = null;
+               InputStream in = null;
+               try {
+                       Cipher cipher = createCipher();
+                       Node node;
+                       if (!session.nodeExists(path)) {
+                               String parentPath = JcrUtils.parentPath(path);
+                               if (!session.nodeExists(parentPath))
+                                       throw new ArgeoJcrException("No parent node of " + path);
+                               Node parentNode = session.getNode(parentPath);
+                               node = parentNode.addNode(JcrUtils.nodeNameFromPath(path));
+                       } else {
+                               node = session.getNode(path);
+                       }
+                       node.addMixin(ArgeoTypes.ARGEO_ENCRYPTED);
+                       SecureRandom random = new SecureRandom();
+                       byte[] iv = new byte[16];
+                       random.nextBytes(iv);
+                       cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
+                       JcrUtils.setBinaryAsBytes(node, ARGEO_IV, iv);
+
+                       in = new CipherInputStream(unencrypted, cipher);
+                       binary = session.getValueFactory().createBinary(in);
+                       node.setProperty(Property.JCR_DATA, binary);
+                       session.save();
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot encrypt", e);
+               } finally {
+                       IOUtils.closeQuietly(unencrypted);
+                       IOUtils.closeQuietly(in);
+                       JcrUtils.closeQuietly(binary);
+               }
+       }
+
+       @Override
+       protected synchronized InputStream decrypt(String path) {
+               Binary binary = null;
+               InputStream encrypted = null;
+               Reader reader = null;
+               try {
+                       if (!session.nodeExists(path)) {
+                               char[] password = ask();
+                               reader = new CharArrayReader(password);
+                               return new ByteArrayInputStream(IOUtils.toByteArray(reader));
+                       } else {
+                               // should be called first for lazy initialisation
+                               SecretKey secretKey = getSecretKey();
+
+                               Cipher cipher = createCipher();
+
+                               Node node = session.getNode(path);
+                               if (node.hasProperty(ARGEO_IV)) {
+                                       byte[] iv = JcrUtils.getBinaryAsBytes(node.getProperty(ARGEO_IV));
+                                       cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
+                               } else {
+                                       cipher.init(Cipher.DECRYPT_MODE, secretKey);
+                               }
+
+                               binary = node.getProperty(Property.JCR_DATA).getBinary();
+                               encrypted = binary.getStream();
+                               return new CipherInputStream(encrypted, cipher);
+                       }
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot decrypt", e);
+               } finally {
+                       IOUtils.closeQuietly(encrypted);
+                       IOUtils.closeQuietly(reader);
+                       JcrUtils.closeQuietly(binary);
+               }
+       }
+
+       protected Cipher createCipher() {
+               try {
+                       Node userHome = UserJcrUtils.getUserHome(session);
+                       if (!userHome.hasNode(ARGEO_KEYRING))
+                               throw new ArgeoJcrException("Keyring not setup");
+                       Node keyring = userHome.getNode(ARGEO_KEYRING);
+                       String cipherName = keyring.getProperty(ARGEO_CIPHER).getString();
+                       Provider securityProvider = getSecurityProvider();
+                       Cipher cipher;
+                       if (securityProvider == null)// TODO use BC?
+                               cipher = Cipher.getInstance(cipherName);
+                       else
+                               cipher = Cipher.getInstance(cipherName, securityProvider);
+                       return cipher;
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot get cipher", e);
+               }
+       }
+
+       public synchronized void changePassword(char[] oldPassword, char[] newPassword) {
+               // TODO decrypt with old pw / encrypt with new pw all argeo:encrypted
+       }
+
+       public synchronized void setSession(Session session) {
+               this.session = session;
+       }
+
+       public void setIterationCountFactor(Integer iterationCountFactor) {
+               this.iterationCountFactor = iterationCountFactor;
+       }
+
+       public void setSecreteKeyLength(Long keyLength) {
+               this.secreteKeyLength = keyLength;
+       }
+
+       public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
+               this.secreteKeyFactoryName = secreteKeyFactoryName;
+       }
+
+       public void setSecreteKeyEncryption(String secreteKeyEncryption) {
+               this.secreteKeyEncryption = secreteKeyEncryption;
+       }
+
+       public void setCipherName(String cipherName) {
+               this.cipherName = cipherName;
+       }
+
+}
\ No newline at end of file
diff --git a/org.argeo.jcr/src/org/argeo/jcr/spring/ThreadBoundSession.java b/org.argeo.jcr/src/org/argeo/jcr/spring/ThreadBoundSession.java
new file mode 100644 (file)
index 0000000..35f0215
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr.spring;
+
+import org.argeo.jcr.ThreadBoundJcrSessionFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+
+@SuppressWarnings("rawtypes")
+@Deprecated
+public class ThreadBoundSession extends ThreadBoundJcrSessionFactory implements FactoryBean, InitializingBean, DisposableBean{
+       public void afterPropertiesSet() throws Exception {
+               init();
+       }
+
+       public void destroy() throws Exception {
+               dispose();
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/tabular/JcrTabularRowIterator.java b/org.argeo.jcr/src/org/argeo/jcr/tabular/JcrTabularRowIterator.java
new file mode 100644 (file)
index 0000000..d4ffbf8
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr.tabular;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.jcr.ArgeoJcrException;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.util.CsvParser;
+import org.argeo.util.tabular.ArrayTabularRow;
+import org.argeo.util.tabular.TabularColumn;
+import org.argeo.util.tabular.TabularRow;
+import org.argeo.util.tabular.TabularRowIterator;
+
+/** Iterates over the rows of a {@link ArgeoTypes#ARGEO_TABLE} node. */
+public class JcrTabularRowIterator implements TabularRowIterator {
+       private Boolean hasNext = null;
+       private Boolean parsingCompleted = false;
+
+       private Long currentRowNumber = 0l;
+
+       private List<TabularColumn> header = new ArrayList<TabularColumn>();
+
+       /** referenced so that we can close it */
+       private Binary binary;
+       private InputStream in;
+
+       private CsvParser csvParser;
+       private ArrayBlockingQueue<List<String>> textLines;
+
+       public JcrTabularRowIterator(Node tableNode) {
+               try {
+                       for (NodeIterator it = tableNode.getNodes(); it.hasNext();) {
+                               Node node = it.nextNode();
+                               if (node.isNodeType(ArgeoTypes.ARGEO_COLUMN)) {
+                                       Integer type = PropertyType.valueFromName(node.getProperty(
+                                                       Property.JCR_REQUIRED_TYPE).getString());
+                                       TabularColumn tc = new TabularColumn(node.getProperty(
+                                                       Property.JCR_TITLE).getString(), type);
+                                       header.add(tc);
+                               }
+                       }
+                       Node contentNode = tableNode.getNode(Property.JCR_CONTENT);
+                       if (contentNode.isNodeType(ArgeoTypes.ARGEO_CSV)) {
+                               textLines = new ArrayBlockingQueue<List<String>>(1000);
+                               csvParser = new CsvParser() {
+                                       protected void processLine(Integer lineNumber,
+                                                       List<String> header, List<String> tokens) {
+                                               try {
+                                                       textLines.put(tokens);
+                                               } catch (InterruptedException e) {
+                                                       // TODO Auto-generated catch block
+                                                       e.printStackTrace();
+                                               }
+                                               // textLines.add(tokens);
+                                               if (hasNext == null) {
+                                                       hasNext = true;
+                                                       synchronized (JcrTabularRowIterator.this) {
+                                                               JcrTabularRowIterator.this.notifyAll();
+                                                       }
+                                               }
+                                       }
+                               };
+                               csvParser.setNoHeader(true);
+                               binary = contentNode.getProperty(Property.JCR_DATA).getBinary();
+                               in = binary.getStream();
+                               Thread thread = new Thread(contentNode.getPath() + " reader") {
+                                       public void run() {
+                                               try {
+                                                       csvParser.parse(in);
+                                               } finally {
+                                                       parsingCompleted = true;
+                                                       IOUtils.closeQuietly(in);
+                                               }
+                                       }
+                               };
+                               thread.start();
+                       }
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot read table " + tableNode, e);
+               }
+       }
+
+       public synchronized boolean hasNext() {
+               // we don't know if there is anything available
+               // while (hasNext == null)
+               // try {
+               // wait();
+               // } catch (InterruptedException e) {
+               // // silent
+               // // FIXME better deal with interruption
+               // Thread.currentThread().interrupt();
+               // break;
+               // }
+
+               // buffer not empty
+               if (!textLines.isEmpty())
+                       return true;
+
+               // maybe the parsing is finished but the flag has not been set
+               while (!parsingCompleted && textLines.isEmpty())
+                       try {
+                               wait(100);
+                       } catch (InterruptedException e) {
+                               // silent
+                               // FIXME better deal with interruption
+                               Thread.currentThread().interrupt();
+                               break;
+                       }
+
+               // buffer not empty
+               if (!textLines.isEmpty())
+                       return true;
+
+               // (parsingCompleted && textLines.isEmpty())
+               return false;
+
+               // if (!hasNext && textLines.isEmpty()) {
+               // if (in != null) {
+               // IOUtils.closeQuietly(in);
+               // in = null;
+               // }
+               // if (binary != null) {
+               // JcrUtils.closeQuietly(binary);
+               // binary = null;
+               // }
+               // return false;
+               // } else
+               // return true;
+       }
+
+       public synchronized TabularRow next() {
+               try {
+                       List<String> tokens = textLines.take();
+                       List<Object> objs = new ArrayList<Object>(tokens.size());
+                       for (String token : tokens) {
+                               // TODO convert to other formats using header
+                               objs.add(token);
+                       }
+                       currentRowNumber++;
+                       return new ArrayTabularRow(objs);
+               } catch (InterruptedException e) {
+                       // silent
+                       // FIXME better deal with interruption
+               }
+               return null;
+       }
+
+       public void remove() {
+               throw new UnsupportedOperationException();
+       }
+
+       public Long getCurrentRowNumber() {
+               return currentRowNumber;
+       }
+
+       public List<TabularColumn> getHeader() {
+               return header;
+       }
+
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/tabular/JcrTabularWriter.java b/org.argeo.jcr/src/org/argeo/jcr/tabular/JcrTabularWriter.java
new file mode 100644 (file)
index 0000000..c3fd97c
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr.tabular;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.List;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.jcr.ArgeoJcrException;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.util.CsvWriter;
+import org.argeo.util.tabular.TabularColumn;
+import org.argeo.util.tabular.TabularWriter;
+
+/** Write / reference tabular content in a JCR repository. */
+public class JcrTabularWriter implements TabularWriter {
+       private Node contentNode;
+       private ByteArrayOutputStream out;
+       private CsvWriter csvWriter;
+       
+       @SuppressWarnings("unused")
+       private final List<TabularColumn> columns;
+
+       /** Creates a table node */
+       public JcrTabularWriter(Node tableNode, List<TabularColumn> columns,
+                       String contentNodeType) {
+               try {
+                       this.columns = columns;
+                       for (TabularColumn column : columns) {
+                               String normalized = JcrUtils.replaceInvalidChars(column
+                                               .getName());
+                               Node columnNode = tableNode.addNode(normalized,
+                                               ArgeoTypes.ARGEO_COLUMN);
+                               columnNode.setProperty(Property.JCR_TITLE, column.getName());
+                               if (column.getType() != null)
+                                       columnNode.setProperty(Property.JCR_REQUIRED_TYPE,
+                                                       PropertyType.nameFromValue(column.getType()));
+                               else
+                                       columnNode.setProperty(Property.JCR_REQUIRED_TYPE,
+                                                       PropertyType.TYPENAME_STRING);
+                       }
+                       contentNode = tableNode.addNode(Property.JCR_CONTENT,
+                                       contentNodeType);
+                       if (contentNodeType.equals(ArgeoTypes.ARGEO_CSV)) {
+                               contentNode.setProperty(Property.JCR_MIMETYPE, "text/csv");
+                               contentNode.setProperty(Property.JCR_ENCODING, "UTF-8");
+                               out = new ByteArrayOutputStream();
+                               csvWriter = new CsvWriter(out);
+                       }
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot create table node " + tableNode, e);
+               }
+       }
+
+       public void appendRow(Object[] row) {
+               csvWriter.writeLine(row);
+       }
+
+       public void close() {
+               Binary binary = null;
+               InputStream in = null;
+               try {
+                       // TODO parallelize with pipes and writing from another thread
+                       in = new ByteArrayInputStream(out.toByteArray());
+                       binary = contentNode.getSession().getValueFactory()
+                                       .createBinary(in);
+                       contentNode.setProperty(Property.JCR_DATA, binary);
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot store data in " + contentNode, e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+                       JcrUtils.closeQuietly(binary);
+               }
+       }
+}
diff --git a/org.argeo.jcr/src/org/argeo/jcr/unit/AbstractJcrTestCase.java b/org.argeo.jcr/src/org/argeo/jcr/unit/AbstractJcrTestCase.java
new file mode 100644 (file)
index 0000000..1269a3e
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.jcr.unit;
+
+import java.io.File;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.jcr.Repository;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.jcr.ArgeoJcrException;
+
+import junit.framework.TestCase;
+
+public abstract class AbstractJcrTestCase extends TestCase {
+       private final static Log log = LogFactory.getLog(AbstractJcrTestCase.class);
+
+       private Repository repository;
+       private Session session = null;
+
+       public final static String LOGIN_CONTEXT_TEST_SYSTEM = "TEST_JACKRABBIT_ADMIN";
+
+       // protected abstract File getRepositoryFile() throws Exception;
+
+       protected abstract Repository createRepository() throws Exception;
+
+       protected abstract void clearRepository(Repository repository)
+                       throws Exception;
+
+       @Override
+       protected void setUp() throws Exception {
+               File homeDir = getHomeDir();
+               FileUtils.deleteDirectory(homeDir);
+               repository = createRepository();
+       }
+
+       @Override
+       protected void tearDown() throws Exception {
+               if (session != null) {
+                       session.logout();
+                       if (log.isTraceEnabled())
+                               log.trace("Logout session");
+               }
+               clearRepository(repository);
+       }
+
+       protected Session session() {
+               if (session != null && session.isLive())
+                       return session;
+               Session session;
+               if (getLoginContext() != null) {
+                       LoginContext lc;
+                       try {
+                               lc = new LoginContext(getLoginContext());
+                               lc.login();
+                       } catch (LoginException e) {
+                               throw new ArgeoJcrException("JAAS login failed", e);
+                       }
+                       session = Subject.doAs(lc.getSubject(),
+                                       new PrivilegedAction<Session>() {
+
+                                               @Override
+                                               public Session run() {
+                                                       return login();
+                                               }
+
+                                       });
+               } else
+                       session = login();
+               this.session = session;
+               return this.session;
+       }
+
+       protected String getLoginContext() {
+               return null;
+       }
+
+       protected Session login() {
+               try {
+                       if (log.isTraceEnabled())
+                               log.trace("Login session");
+                       Subject subject = Subject.getSubject(AccessController.getContext());
+                       if (subject != null)
+                               return getRepository().login();
+                       else
+                               return getRepository().login(
+                                               new SimpleCredentials("demo", "demo".toCharArray()));
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot login to repository", e);
+               }
+       }
+
+       protected Repository getRepository() {
+               return repository;
+       }
+
+       /**
+        * enables children class to set an existing repository in case it is not
+        * deleted on startup, to test migration by instance
+        */
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       protected File getHomeDir() {
+               File homeDir = new File(System.getProperty("java.io.tmpdir"),
+                               AbstractJcrTestCase.class.getSimpleName() + "-"
+                                               + System.getProperty("user.name"));
+               return homeDir;
+       }
+
+}
diff --git a/org.argeo.node.api/.classpath b/org.argeo.node.api/.classpath
new file mode 100644 (file)
index 0000000..eca7bdb
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.argeo.node.api/.project b/org.argeo.node.api/.project
new file mode 100644 (file)
index 0000000..9573f0c
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.node.api</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/org.argeo.node.api/bnd.bnd b/org.argeo.node.api/bnd.bnd
new file mode 100644 (file)
index 0000000..52a2bdd
--- /dev/null
@@ -0,0 +1 @@
+Provide-Capability: cms.datamodel;name=node;cnd=/org/argeo/node/node.cnd
\ No newline at end of file
diff --git a/org.argeo.node.api/build.properties b/org.argeo.node.api/build.properties
new file mode 100644 (file)
index 0000000..34d2e4d
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .
diff --git a/org.argeo.node.api/pom.xml b/org.argeo.node.api/pom.xml
new file mode 100644 (file)
index 0000000..c52d9a4
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <artifactId>argeo-commons</artifactId>
+               <version>2.1.46-SNAPSHOT</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.node.api</artifactId>
+       <name>Argeo Node API</name>
+       <packaging>jar</packaging>
+       <dependencies>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/org.argeo.node.api/src/org/argeo/node/ArgeoLogListener.java b/org.argeo.node.api/src/org/argeo/node/ArgeoLogListener.java
new file mode 100644 (file)
index 0000000..698dfe1
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.node;
+
+/** Framework agnostic interface for log notifications */
+public interface ArgeoLogListener {
+       /**
+        * Appends a log
+        * 
+        * @param username
+        *            authentified user, null for anonymous
+        * @param level
+        *            INFO, DEBUG, WARN, etc. (logging framework specific)
+        * @param category
+        *            hierarchy (logging framework specific)
+        * @param thread
+        *            name of the thread which logged this message
+        * @param msg
+        *            any object as long as its toString() method returns the
+        *            message
+        * @param the
+        *            exception in log4j ThrowableStrRep format
+        */
+       public void appendLog(String username, Long timestamp, String level,
+                       String category, String thread, Object msg, String[] exception);
+}
diff --git a/org.argeo.node.api/src/org/argeo/node/ArgeoLogger.java b/org.argeo.node.api/src/org/argeo/node/ArgeoLogger.java
new file mode 100644 (file)
index 0000000..213286d
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.node;
+
+/**
+ * Logging framework agnostic identifying a logging service, to which one can
+ * register
+ */
+public interface ArgeoLogger {
+       /**
+        * Register for events by threads with the same authentication (or all
+        * threads if admin)
+        */
+       public void register(ArgeoLogListener listener,
+                       Integer numberOfPreviousEvents);
+
+       /**
+        * For admin use only: register for all users
+        * 
+        * @param listener
+        *            the log listener
+        * @param numberOfPreviousEvents
+        *            the number of previous events to notify
+        * @param everything
+        *            if true even anonymous is logged
+        */
+       public void registerForAll(ArgeoLogListener listener,
+                       Integer numberOfPreviousEvents, boolean everything);
+
+       public void unregister(ArgeoLogListener listener);
+
+       public void unregisterForAll(ArgeoLogListener listener);
+}
diff --git a/org.argeo.node.api/src/org/argeo/node/DataAdminPrincipal.java b/org.argeo.node.api/src/org/argeo/node/DataAdminPrincipal.java
new file mode 100644 (file)
index 0000000..743c96f
--- /dev/null
@@ -0,0 +1,30 @@
+package org.argeo.node;
+
+import java.security.Principal;
+
+/** Allows to modify any data. */
+public final class DataAdminPrincipal implements Principal {
+       // FIXME put auth constants in API
+       private final String name = "OU=node";
+
+       @Override
+       public String getName() {
+               return name;
+       }
+
+       @Override
+       public int hashCode() {
+               return name.hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               return this == obj;
+       }
+
+       @Override
+       public String toString() {
+               return name.toString();
+       }
+
+}
diff --git a/org.argeo.node.api/src/org/argeo/node/DataModelNamespace.java b/org.argeo.node.api/src/org/argeo/node/DataModelNamespace.java
new file mode 100644 (file)
index 0000000..5784196
--- /dev/null
@@ -0,0 +1,16 @@
+package org.argeo.node;
+
+import org.osgi.resource.Namespace;
+
+/** CMS Data Model capability namespace. */
+public class DataModelNamespace extends Namespace {
+
+       public static final String CMS_DATA_MODEL_NAMESPACE = "cms.datamodel";
+       public static final String CAPABILITY_NAME_ATTRIBUTE = "name";
+       public static final String CAPABILITY_CND_ATTRIBUTE = "cnd";
+
+       private DataModelNamespace() {
+               // empty
+       }
+
+}
diff --git a/org.argeo.node.api/src/org/argeo/node/EnumAD.java b/org.argeo.node.api/src/org/argeo/node/EnumAD.java
new file mode 100644 (file)
index 0000000..1ee6d39
--- /dev/null
@@ -0,0 +1,59 @@
+package org.argeo.node;
+
+import org.osgi.service.metatype.AttributeDefinition;
+
+interface EnumAD extends AttributeDefinition {
+       String name();
+
+       default Object getDefault() {
+               return null;
+       }
+
+       @Override
+       default String getName() {
+               return name();
+       }
+
+       @Override
+       default String getID() {
+               return getClass().getName() + "." + name();
+       }
+
+       @Override
+       default String getDescription() {
+               return null;
+       }
+
+       @Override
+       default int getCardinality() {
+               return 0;
+       }
+
+       @Override
+       default int getType() {
+               return STRING;
+       }
+
+       @Override
+       default String[] getOptionValues() {
+               return null;
+       }
+
+       @Override
+       default String[] getOptionLabels() {
+               return null;
+       }
+
+       @Override
+       default String validate(String value) {
+               return null;
+       }
+
+       @Override
+       default String[] getDefaultValue() {
+               Object value = getDefault();
+               if (value == null)
+                       return null;
+               return new String[] { value.toString() };
+       }
+}
diff --git a/org.argeo.node.api/src/org/argeo/node/EnumOCD.java b/org.argeo.node.api/src/org/argeo/node/EnumOCD.java
new file mode 100644 (file)
index 0000000..daa2f54
--- /dev/null
@@ -0,0 +1,50 @@
+package org.argeo.node;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+
+import org.osgi.service.metatype.AttributeDefinition;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+class EnumOCD<T extends Enum<T>> implements ObjectClassDefinition {
+       private final Class<T> enumClass;
+       private String locale;
+
+       public EnumOCD(Class<T> clazz, String locale) {
+               this.enumClass = clazz;
+               this.locale = locale;
+       }
+
+       @Override
+       public String getName() {
+               return null;
+       }
+
+       @Override
+       public String getID() {
+               return enumClass.getName();
+       }
+
+       @Override
+       public String getDescription() {
+               return null;
+       }
+
+       @Override
+       public AttributeDefinition[] getAttributeDefinitions(int filter) {
+               EnumSet<T> set = EnumSet.allOf(enumClass);
+               List<AttributeDefinition> attrs = new ArrayList<>();
+               for (T key : set)
+                       attrs.add((AttributeDefinition) key);
+               return attrs.toArray(new AttributeDefinition[attrs.size()]);
+       }
+
+       @Override
+       public InputStream getIcon(int size) throws IOException {
+               return null;
+       }
+
+}
diff --git a/org.argeo.node.api/src/org/argeo/node/NodeConstants.java b/org.argeo.node.api/src/org/argeo/node/NodeConstants.java
new file mode 100644 (file)
index 0000000..477b0b9
--- /dev/null
@@ -0,0 +1,50 @@
+package org.argeo.node;
+
+public interface NodeConstants {
+       /*
+        * PIDs
+        */
+       String NODE_STATE_PID = "org.argeo.node.state";
+       String NODE_DEPLOYMENT_PID = "org.argeo.node.deployment";
+       String NODE_INSTANCE_PID = "org.argeo.node.instance";
+
+//     String NODE_REPO_PID = "org.argeo.node.repo";
+       String NODE_USER_ADMIN_PID = "org.argeo.node.userAdmin";
+
+       /*
+        * FACTORY PIDs
+        */
+       String NODE_REPOS_FACTORY_PID = "org.argeo.node.repos";
+       String NODE_USER_DIRECTORIES_FACTORY_PID = "org.argeo.node.userDirectories";
+
+       /*
+        * DEPLOY
+        */
+       String DEPLOY_BASEDN = "ou=deploy,ou=node";
+
+       /*
+        * FRAMEWORK PROPERTIES
+        */
+       String NODE_INIT = "argeo.node.init";
+       String I18N_DEFAULT_LOCALE = "argeo.i18n.defaultLocale";
+       String I18N_LOCALES = "argeo.i18n.locales";
+       // Node Security
+       String ROLES_URI = "argeo.node.roles.uri";
+       /** URI to an LDIF file or LDAP server used as initialization or backend */
+       String USERADMIN_URIS = "argeo.node.useradmin.uris";
+       // Node
+       /** Properties configuring the node repository */
+       String NODE_REPO_PROP_PREFIX = "argeo.node.repo.";
+
+       /*
+        * STANDARD ATTRIBUTES
+        */
+       String CN = "cn";
+       String OU = "ou";
+       String LABELED_URI = "labeledUri";
+       
+       /*
+        * STANDARD VALUES
+        */
+       String DEFAULT = "default";
+}
diff --git a/org.argeo.node.api/src/org/argeo/node/NodeDeployment.java b/org.argeo.node.api/src/org/argeo/node/NodeDeployment.java
new file mode 100644 (file)
index 0000000..8e5558d
--- /dev/null
@@ -0,0 +1,5 @@
+package org.argeo.node;
+
+public interface NodeDeployment {
+       Long getAvailableSince();
+}
diff --git a/org.argeo.node.api/src/org/argeo/node/NodeInstance.java b/org.argeo.node.api/src/org/argeo/node/NodeInstance.java
new file mode 100644 (file)
index 0000000..e1e4bcd
--- /dev/null
@@ -0,0 +1,5 @@
+package org.argeo.node;
+
+public interface NodeInstance {
+
+}
diff --git a/org.argeo.node.api/src/org/argeo/node/NodeOID.java b/org.argeo.node.api/src/org/argeo/node/NodeOID.java
new file mode 100644 (file)
index 0000000..9d8ff3d
--- /dev/null
@@ -0,0 +1,18 @@
+package org.argeo.node;
+
+interface NodeOID {
+       String BASE = "1.3.6.1.4.1" + ".48308" + ".1";
+
+       // ATTRIBUTE TYPES
+       String ATTRIBUTE_TYPES = BASE + ".4";
+       String URI = ATTRIBUTE_TYPES + ".1";
+       String HTTP_PORT = ATTRIBUTE_TYPES + ".2";
+       String HTTPS_PORT = ATTRIBUTE_TYPES + ".3";
+
+       // OBJECT CLASSES
+       String OBJECT_CLASSES = BASE + ".6";
+       String JCR_REPOSITORY = OBJECT_CLASSES + ".1";
+
+       // EXTERNAL
+       String LABELED_URI = "1.3.6.1.4.1.250.1.57";
+}
diff --git a/org.argeo.node.api/src/org/argeo/node/NodeState.java b/org.argeo.node.api/src/org/argeo/node/NodeState.java
new file mode 100644 (file)
index 0000000..d7148c6
--- /dev/null
@@ -0,0 +1,17 @@
+package org.argeo.node;
+
+import java.util.List;
+import java.util.Locale;
+
+public interface NodeState {
+       Locale getDefaultLocale();
+
+       List<Locale> getLocales();
+
+       String getHostname();
+
+       boolean isClean();
+       
+       Long getAvailableSince();
+
+}
diff --git a/org.argeo.node.api/src/org/argeo/node/RepoConf.java b/org.argeo.node.api/src/org/argeo/node/RepoConf.java
new file mode 100644 (file)
index 0000000..be4f6f7
--- /dev/null
@@ -0,0 +1,66 @@
+package org.argeo.node;
+
+/** JCR repository configuration */
+public enum RepoConf implements EnumAD {
+       /** Repository type */
+       type("localfs"),
+       /** Default workspace */
+       @Deprecated
+       defaultWorkspace("main"),
+       /** Database URL */
+       dburl(null),
+       /** Database user */
+       dbuser(null),
+       /** Database password */
+       dbpassword(null),
+
+       /** The identifier (can be an URL locating the repo) */
+       labeledUri(null),
+       //
+       // JACKRABBIT SPECIFIC
+       //
+       /** Maximum database pool size */
+       maxPoolSize(10),
+       /** Maximum cache size in MB */
+       @Deprecated
+       maxCacheMB(null),
+       /** Bundle cache size in MB */
+       bundleCacheMB(8),
+       /** Extractor pool size */
+       extractorPoolSize(0),
+       /** Search cache size */
+       searchCacheSize(1000),
+       /** Max volatile index size */
+       maxVolatileIndexSize(1048576);
+
+       /** The default value. */
+       private Object def;
+       private String oid;
+
+       RepoConf(String oid, Object def) {
+               this.oid = oid;
+               this.def = def;
+       }
+
+       RepoConf(Object def) {
+               this.def = def;
+       }
+
+       public Object getDefault() {
+               return def;
+       }
+
+       @Override
+       public String getID() {
+               if (oid != null)
+                       return oid;
+               return EnumAD.super.getID();
+       }
+
+       public static class OCD extends EnumOCD<RepoConf> {
+               public OCD(String locale) {
+                       super(RepoConf.class, locale);
+               }
+       }
+
+}
diff --git a/org.argeo.node.api/src/org/argeo/node/node.cnd b/org.argeo.node.api/src/org/argeo/node/node.cnd
new file mode 100644 (file)
index 0000000..fbfea9d
--- /dev/null
@@ -0,0 +1,72 @@
+<argeo = 'http://www.argeo.org/ns/argeo'>
+
+// GENERIC TYPES NOT AVAILABLE IN JCR
+[argeo:link] > mix:created, mix:lastModified
+mixin
+// URI(s)
+- argeo:uri (STRING) m
+
+[argeo:references] > nt:unstructured
+- * (REFERENCE) *
+
+// DATA MODEL
+[argeo:dataModel] > mix:created, mix:lastModified, mix:versionable
+mixin
+- argeo:uri (STRING) m
+- argeo:dataModelVersion (STRING) m
+
+// USER NODES
+// user should be lower case, between 3 and 15 characters long
+[argeo:userHome] > mix:created, mix:lastModified
+mixin
+- argeo:userID (STRING) m
+- argeo:remoteRoles (STRING) *
+// deprecated. for backward compatibility:
++ argeo:profile (argeo:userProfile)
++ argeo:keyring (argeo:pbeSpec)
++ argeo:preferences (argeo:preferenceNode)
+
+[argeo:userProfile] > mix:created, mix:lastModified, mix:title, mix:versionable
+mixin
+- argeo:userID (STRING) m
+- argeo:enabled (BOOLEAN)
+- argeo:accountNonExpired (BOOLEAN)
+- argeo:accountNonLocked (BOOLEAN)
+- argeo:credentialsNonExpired (BOOLEAN)
+
+[argeo:preferenceNode] > mix:lastModified, mix:versionable
+mixin
++ * (argeo:preferenceNode) * version
+
+[argeo:remoteRepository] > nt:unstructured
+- argeo:uri (STRING)
+- argeo:userID (STRING)
++ argeo:password (argeo:encrypted)
+
+// TABULAR CONTENT
+[argeo:table] > nt:file
++ * (argeo:column) *
+
+[argeo:column] > mix:title
+- jcr:requiredType (STRING) = 'STRING'
+
+[argeo:csv] > nt:resource
+
+// CRYPTO
+[argeo:encrypted] > nt:base
+mixin
+// initialization vector used by some algorithms
+- argeo:iv (BINARY)
+
+[argeo:pbeKeySpec] > nt:base
+mixin
+- argeo:secretKeyFactory (STRING)
+- argeo:salt (BINARY)
+- argeo:iterationCount (LONG)
+- argeo:keyLength (LONG)
+- argeo:secretKeyEncryption (STRING)
+
+[argeo:pbeSpec] > argeo:pbeKeySpec
+mixin
+- argeo:cipher (STRING)
+
diff --git a/org.argeo.node.api/src/org/argeo/node/package-info.java b/org.argeo.node.api/src/org/argeo/node/package-info.java
new file mode 100644 (file)
index 0000000..fda3bae
--- /dev/null
@@ -0,0 +1,5 @@
+/**
+ * Abstractions or constants related to an Argeo Node, an active repository of
+ * linked data.
+ */
+package org.argeo.node;
\ No newline at end of file
diff --git a/org.argeo.node.api/src/org/argeo/node/packageinfo b/org.argeo.node.api/src/org/argeo/node/packageinfo
new file mode 100644 (file)
index 0000000..2c9afe8
--- /dev/null
@@ -0,0 +1 @@
+version 2.1.0
\ No newline at end of file
index 607f6264b2c9c76809759b1ac4bae43d3330f71d..64ace0c3ee4f8fc518d41a0c9ee67af126e70f22 100644 (file)
@@ -205,10 +205,13 @@ public class OsgiBoot implements OsgiBootConstants {
                                        warn("Skip " + url);
                                return;
                        } else {
-
                                Bundle bundle = bundleContext.installBundle(url);
-                               OsgiBootUtils
-                                               .info("Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
+                               if (url.startsWith("http"))
+                                       OsgiBootUtils
+                                                       .info("Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
+                               else if (debug)
+                                       OsgiBootUtils.debug(
+                                                       "Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
                        }
                } catch (BundleException e) {
                        String message = e.getMessage();
@@ -642,14 +645,14 @@ public class OsgiBoot implements OsgiBootConstants {
                return (basePath + '/' + relativePath).replace('/', File.separatorChar);
        }
 
-       private String removeFilePrefix(String url) {
-               if (url.startsWith("file:"))
-                       return url.substring("file:".length());
-               else if (url.startsWith("reference:file:"))
-                       return url.substring("reference:file:".length());
-               else
-                       return url;
-       }
+       // private String removeFilePrefix(String url) {
+       // if (url.startsWith("file:"))
+       // return url.substring("file:".length());
+       // else if (url.startsWith("reference:file:"))
+       // return url.substring("reference:file:".length());
+       // else
+       // return url;
+       // }
 
        /*
         * BEAN METHODS
diff --git a/org.argeo.security.core/.classpath b/org.argeo.security.core/.classpath
deleted file mode 100644 (file)
index 4e5da1d..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="src" path="src" />
-       <classpathentry kind="src" path="ext/test" />
-       <classpathentry kind="con"
-               path="org.eclipse.pde.core.requiredPlugins" />
-       <classpathentry kind="con"
-               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" />
-       <classpathentry kind="output" path="bin" />
-</classpath>
diff --git a/org.argeo.security.core/.project b/org.argeo.security.core/.project
deleted file mode 100644 (file)
index a8e385a..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.security.core</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-               <nature>org.eclipse.pde.PluginNature</nature>
-       </natures>
-</projectDescription>
diff --git a/org.argeo.security.core/bnd.bnd b/org.argeo.security.core/bnd.bnd
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/org.argeo.security.core/build.properties b/org.argeo.security.core/build.properties
deleted file mode 100644 (file)
index af03ba4..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-source.. = src/,\
-           ext/test/
-additional.bundles = org.junit,\
-                     org.slf4j.commons.logging,\
-                     org.slf4j.api,\
-                     org.slf4j.log4j12,\
-                     org.apache.log4j,\
-                     bitronix.tm
diff --git a/org.argeo.security.core/ext/test/log4j.properties b/org.argeo.security.core/ext/test/log4j.properties
deleted file mode 100644 (file)
index ef73566..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-log4j.rootLogger=WARN, console
-
-## Levels
-log4j.logger.org.argeo=TRACE
-
-log4j.logger.org.hibernate=WARN
-
-log4j.logger.org.springframework=WARN
-#log4j.logger.org.springframework.web=DEBUG
-#log4j.logger.org.springframework.jms=WARN
-#log4j.logger.org.springframework.security=WARN
-
-log4j.logger.org.apache.activemq=WARN
-log4j.logger.org.apache.activemq.transport=WARN
-log4j.logger.org.apache.activemq.ActiveMQMessageConsumer=INFO
-log4j.logger.org.apache.activemq.ActiveMQMessageProducer=INFO
-
-log4j.logger.org.apache.catalina=INFO
-log4j.logger.org.apache.coyote=INFO
-log4j.logger.org.apache.tomcat=INFO
-
-## Appenders
-# console is set to be a ConsoleAppender.
-log4j.appender.console=org.apache.log4j.ConsoleAppender
-
-# console uses PatternLayout.
-log4j.appender.console.layout=org.apache.log4j.PatternLayout
-log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n
diff --git a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/BasicTestConstants.java b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/BasicTestConstants.java
deleted file mode 100644 (file)
index 98b8bc9..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-interface BasicTestConstants {
-       String BASE_DN = "dc=example,dc=com";
-       String ROOT_USER_DN = "uid=root,ou=users," + BASE_DN;
-       String DEMO_USER_DN = "uid=demo,ou=users," + BASE_DN;
-       String ADMIN_GROUP_DN = "cn=admin,ou=groups," + BASE_DN;
-       String EDITORS_GROUP_DN = "cn=editors,ou=groups," + BASE_DN;
-}
diff --git a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifParserTest.java
deleted file mode 100644 (file)
index fecf5dd..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.List;
-import java.util.SortedMap;
-
-import javax.naming.NamingEnumeration;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.ldap.LdapName;
-
-import org.argeo.util.naming.LdifParser;
-
-import junit.framework.TestCase;
-
-public class LdifParserTest extends TestCase implements BasicTestConstants {
-       public void testBasicLdif() throws Exception {
-               LdifParser ldifParser = new LdifParser();
-               SortedMap<LdapName, Attributes> res = ldifParser.read(getClass()
-                               .getResourceAsStream("basic.ldif"));
-               LdapName rootDn = new LdapName(ROOT_USER_DN);
-               Attributes rootAttributes = res.get(rootDn);
-               assertNotNull(rootAttributes);
-               assertEquals("Superuser",
-                               rootAttributes.get(LdifName.description.name()).get());
-               byte[] rawPwEntry = (byte[]) rootAttributes.get(
-                               LdifName.userPassword.name()).get();
-               assertEquals("{SHA}ieSV55Qc+eQOaYDRSha/AjzNTJE=",
-                               new String(rawPwEntry));
-               byte[] hashedPassword = DigestUtils.sha1("demo".getBytes());
-               assertEquals("{SHA}" + Base64.getEncoder().encodeToString(hashedPassword),
-                               new String(rawPwEntry));
-
-               LdapName adminDn = new LdapName(ADMIN_GROUP_DN);
-               Attributes adminAttributes = res.get(adminDn);
-               assertNotNull(adminAttributes);
-               Attribute memberAttribute = adminAttributes.get(LdifName.member.name());
-               assertNotNull(memberAttribute);
-               NamingEnumeration<?> members = memberAttribute.getAll();
-               List<String> users = new ArrayList<String>();
-               while (members.hasMore()) {
-                       Object value = members.next();
-                       users.add(value.toString());
-               }
-               assertEquals(1, users.size());
-               assertEquals(rootDn, new LdapName(users.get(0)));
-       }
-}
diff --git a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java
deleted file mode 100644 (file)
index a8a7d22..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.InputStream;
-import java.net.URI;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.UUID;
-
-import javax.transaction.TransactionManager;
-
-import org.osgi.service.useradmin.Authorization;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-
-import bitronix.tm.BitronixTransactionManager;
-import bitronix.tm.TransactionManagerServices;
-import bitronix.tm.resource.ehcache.EhCacheXAResourceProducer;
-import junit.framework.TestCase;
-
-public class LdifUserAdminTest extends TestCase implements BasicTestConstants {
-       private BitronixTransactionManager tm;
-       private URI uri;
-       private AbstractUserDirectory userAdmin;
-
-       public void testConcurrent() throws Exception {
-       }
-
-       @SuppressWarnings("unchecked")
-       public void testEdition() throws Exception {
-               User demoUser = (User) userAdmin.getRole(DEMO_USER_DN);
-               assertNotNull(demoUser);
-
-               tm.begin();
-               String newName = "demo";
-               demoUser.getProperties().put("cn", newName);
-               assertEquals(newName, demoUser.getProperties().get("cn"));
-               tm.commit();
-               persistAndRestart();
-               assertEquals(newName, demoUser.getProperties().get("cn"));
-
-               tm.begin();
-               userAdmin.removeRole(DEMO_USER_DN);
-               tm.commit();
-               persistAndRestart();
-
-               // check data
-               Role[] search = userAdmin.getRoles("(objectclass=inetOrgPerson)");
-               assertEquals(1, search.length);
-               Group editorGroup = (Group) userAdmin.getRole(EDITORS_GROUP_DN);
-               assertNotNull(editorGroup);
-               Role[] members = editorGroup.getMembers();
-               assertEquals(1, members.length);
-       }
-
-       public void testRetrieve() throws Exception {
-               // users
-               User rootUser = (User) userAdmin.getRole(ROOT_USER_DN);
-               assertNotNull(rootUser);
-               User demoUser = (User) userAdmin.getRole(DEMO_USER_DN);
-               assertNotNull(demoUser);
-
-               // groups
-               Group adminGroup = (Group) userAdmin.getRole(ADMIN_GROUP_DN);
-               assertNotNull(adminGroup);
-               Role[] members = adminGroup.getMembers();
-               assertEquals(1, members.length);
-               assertEquals(rootUser, members[0]);
-
-               Group editorGroup = (Group) userAdmin.getRole(EDITORS_GROUP_DN);
-               assertNotNull(editorGroup);
-               members = editorGroup.getMembers();
-               assertEquals(2, members.length);
-               assertEquals(adminGroup, members[0]);
-               assertEquals(demoUser, members[1]);
-
-               Authorization rootAuth = userAdmin.getAuthorization(rootUser);
-               List<String> rootRoles = Arrays.asList(rootAuth.getRoles());
-               assertEquals(3, rootRoles.size());
-               assertTrue(rootRoles.contains(ROOT_USER_DN));
-               assertTrue(rootRoles.contains(ADMIN_GROUP_DN));
-               assertTrue(rootRoles.contains(EDITORS_GROUP_DN));
-
-               // properties
-               assertEquals("root@localhost", rootUser.getProperties().get("mail"));
-
-               // credentials
-               byte[] hashedPassword = ("{SHA}" + Base64.getEncoder().encodeToString(DigestUtils.sha1("demo".getBytes())))
-                               .getBytes();
-               assertTrue(rootUser.hasCredential(LdifName.userPassword.name(), hashedPassword));
-               assertTrue(demoUser.hasCredential(LdifName.userPassword.name(), hashedPassword));
-
-               // search
-               Role[] search = userAdmin.getRoles(null);
-               assertEquals(4, search.length);
-               search = userAdmin.getRoles("(objectClass=groupOfNames)");
-               assertEquals(2, search.length);
-               search = userAdmin.getRoles("(objectclass=inetOrgPerson)");
-               assertEquals(2, search.length);
-               search = userAdmin.getRoles("(&(objectclass=inetOrgPerson)(uid=demo))");
-               assertEquals(1, search.length);
-       }
-
-       public void testReadWriteRead() throws Exception {
-               if (userAdmin instanceof LdifUserAdmin) {
-                       Dictionary<String, Object> props = userAdmin.getProperties();
-                       ByteArrayOutputStream out = new ByteArrayOutputStream();
-                       ((LdifUserAdmin) userAdmin).save(out);
-                       byte[] arr = out.toByteArray();
-                       out.close();
-                       userAdmin.destroy();
-                       // String written = new String(arr);
-                       // System.out.print(written);
-                       try (ByteArrayInputStream in = new ByteArrayInputStream(arr)) {
-                               userAdmin = new LdifUserAdmin(props);
-                               ((LdifUserAdmin) userAdmin).load(in);
-                       }
-                       Role[] search = userAdmin.getRoles(null);
-                       assertEquals(4, search.length);
-               } else {
-                       // test not relevant for LDAP
-               }
-       }
-
-       @Override
-       protected void setUp() throws Exception {
-               Path tempDir = Files.createTempDirectory(getClass().getName());
-               String uriProp = System.getProperty("argeo.userdirectory.uri");
-               if (uriProp != null)
-                       uri = new URI(uriProp);
-               else {
-                       tempDir.toFile().deleteOnExit();
-                       Path ldifPath = tempDir.resolve(BASE_DN + ".ldif");
-                       try (InputStream in = getClass().getResource("basic.ldif").openStream()) {
-                               Files.copy(in, ldifPath);
-                       }
-                       uri = ldifPath.toUri();
-               }
-
-               bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration();
-               tmConf.setServerId(UUID.randomUUID().toString());
-               tmConf.setLogPart1Filename(new File(tempDir.toFile(), "btm1.tlog").getAbsolutePath());
-               tmConf.setLogPart2Filename(new File(tempDir.toFile(), "btm2.tlog").getAbsolutePath());
-               tm = TransactionManagerServices.getTransactionManager();
-
-               userAdmin = initUserAdmin(uri, tm);
-       }
-
-       private AbstractUserDirectory initUserAdmin(URI uri, TransactionManager tm) {
-               Dictionary<String, Object> props = new Hashtable<>();
-               props.put(UserAdminConf.uri.name(), uri.toString());
-               props.put(UserAdminConf.baseDn.name(), BASE_DN);
-               props.put(UserAdminConf.userBase.name(), "ou=users");
-               props.put(UserAdminConf.groupBase.name(), "ou=groups");
-               AbstractUserDirectory userAdmin;
-               if (uri.getScheme().startsWith("ldap"))
-                       userAdmin = new LdapUserAdmin(props);
-               else
-                       userAdmin = new LdifUserAdmin(props);
-               userAdmin.init();
-               // JTA
-               EhCacheXAResourceProducer.registerXAResource(UserDirectory.class.getName(), userAdmin.getXaResource());
-               userAdmin.setTransactionManager(tm);
-               return userAdmin;
-       }
-
-       private void persistAndRestart() {
-               EhCacheXAResourceProducer.unregisterXAResource(UserDirectory.class.getName(), userAdmin.getXaResource());
-               if (userAdmin instanceof LdifUserAdmin)
-                       ((LdifUserAdmin) userAdmin).save();
-               userAdmin.destroy();
-               userAdmin = initUserAdmin(uri, tm);
-       }
-
-       @Override
-       protected void tearDown() throws Exception {
-               EhCacheXAResourceProducer.unregisterXAResource(UserDirectory.class.getName(), userAdmin.getXaResource());
-               tm.shutdown();
-               if (userAdmin != null)
-                       userAdmin.destroy();
-       }
-
-}
diff --git a/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/basic.ldif b/org.argeo.security.core/ext/test/org/argeo/osgi/useradmin/basic.ldif
deleted file mode 100644 (file)
index b7328b0..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-dn: dc=example,dc=com
-objectClass: domain
-objectClass: extensibleObject
-objectClass: top
-dc: example
-
-dn: ou=groups,dc=example,dc=com
-objectClass: organizationalUnit
-objectClass: top
-ou: groups
-
-dn: ou=users,dc=example,dc=com
-objectClass: organizationalUnit
-objectClass: top
-ou: users
-
-dn: uid=demo,ou=users,dc=example,dc=com
-objectClass: inetOrgPerson
-objectClass: organizationalPerson
-objectClass: person
-objectClass: top
-cn: Demo User
-description: Demo user
-givenName: Demo
-mail: demo@localhost
-sn: User
-uid: demo
-userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
-
-dn: uid=root,ou=users,dc=example,dc=com
-objectClass: inetOrgPerson
-objectClass: person
-objectClass: organizationalPerson
-objectClass: top
-cn: Super User
-description: Superuser
-givenName: Super
-mail: root@localhost
-sn: User
-uid: root
-userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
-
-dn: cn=admin,ou=groups,dc=example,dc=com
-objectClass: groupOfNames
-objectClass: top
-cn: admin
-member: uid=root,ou=users,dc=example,dc=com
-
-dn: cn=editors,ou=groups,dc=example,dc=com
-objectClass: groupOfNames
-objectClass: top
-cn: editors
-member: cn=admin,ou=groups,dc=example,dc=com
-member: uid=demo,ou=users,dc=example,dc=com
diff --git a/org.argeo.security.core/pom.xml b/org.argeo.security.core/pom.xml
deleted file mode 100644 (file)
index 56c2cfd..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-       <modelVersion>4.0.0</modelVersion>
-       <parent>
-               <groupId>org.argeo.commons</groupId>
-               <artifactId>argeo-commons</artifactId>
-               <version>2.1.46-SNAPSHOT</version>
-               <relativePath>..</relativePath>
-       </parent>
-       <artifactId>org.argeo.security.core</artifactId>
-       <name>Commons Security</name>
-       <dependencies>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.util</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.server.jcr</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-       </dependencies>
-</project>
\ No newline at end of file
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java
deleted file mode 100644 (file)
index 3f5bf85..0000000
+++ /dev/null
@@ -1,426 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import static org.argeo.osgi.useradmin.LdifName.inetOrgPerson;
-import static org.argeo.osgi.useradmin.LdifName.objectClass;
-import static org.argeo.osgi.useradmin.LdifName.organizationalPerson;
-import static org.argeo.osgi.useradmin.LdifName.person;
-import static org.argeo.osgi.useradmin.LdifName.top;
-
-import java.io.File;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.naming.InvalidNameException;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.BasicAttribute;
-import javax.naming.directory.BasicAttributes;
-import javax.naming.ldap.LdapName;
-import javax.naming.ldap.Rdn;
-import javax.transaction.SystemException;
-import javax.transaction.Transaction;
-import javax.transaction.TransactionManager;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.osgi.framework.Filter;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.useradmin.Authorization;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-
-/** Base class for a {@link UserDirectory}. */
-public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
-       private final static Log log = LogFactory.getLog(AbstractUserDirectory.class);
-
-       private final Hashtable<String, Object> properties;
-       private final LdapName baseDn;
-       private final String userObjectClass, userBase, groupObjectClass, groupBase;
-
-       private final boolean readOnly;
-       private final URI uri;
-
-       private UserAdmin externalRoles;
-       private List<String> indexedUserProperties = Arrays
-                       .asList(new String[] { LdifName.uid.name(), LdifName.mail.name(), LdifName.cn.name() });
-
-       private String memberAttributeId = "member";
-       private List<String> credentialAttributeIds = Arrays.asList(new String[] { LdifName.userPassword.name() });
-
-       // JTA
-       private TransactionManager transactionManager;
-       private WcXaResource xaResource = new WcXaResource(this);
-
-       public AbstractUserDirectory(Dictionary<String, ?> props) {
-               properties = new Hashtable<String, Object>();
-               for (Enumeration<String> keys = props.keys(); keys.hasMoreElements();) {
-                       String key = keys.nextElement();
-                       properties.put(key, props.get(key));
-               }
-
-               String uriStr = UserAdminConf.uri.getValue(properties);
-               if (uriStr == null)
-                       uri = null;
-               else
-                       try {
-                               uri = new URI(uriStr);
-                       } catch (URISyntaxException e) {
-                               throw new UserDirectoryException("Badly formatted URI " + uriStr, e);
-                       }
-
-               try {
-                       baseDn = new LdapName(UserAdminConf.baseDn.getValue(properties));
-               } catch (InvalidNameException e) {
-                       throw new UserDirectoryException("Badly formated base DN " + UserAdminConf.baseDn.getValue(properties), e);
-               }
-               String readOnlyStr = UserAdminConf.readOnly.getValue(properties);
-               if (readOnlyStr == null) {
-                       readOnly = readOnlyDefault(uri);
-                       properties.put(UserAdminConf.readOnly.name(), Boolean.toString(readOnly));
-               } else
-                       readOnly = new Boolean(readOnlyStr);
-
-               userObjectClass = UserAdminConf.userObjectClass.getValue(properties);
-               userBase = UserAdminConf.userBase.getValue(properties);
-               groupObjectClass = UserAdminConf.groupObjectClass.getValue(properties);
-               groupBase = UserAdminConf.groupBase.getValue(properties);
-       }
-
-       /** Returns the groups this user is a direct member of. */
-       protected abstract List<LdapName> getDirectGroups(LdapName dn);
-
-       protected abstract Boolean daoHasRole(LdapName dn);
-
-       protected abstract DirectoryUser daoGetRole(LdapName key);
-
-       protected abstract List<DirectoryUser> doGetRoles(Filter f);
-
-       public void init() {
-
-       }
-
-       public void destroy() {
-
-       }
-
-       protected boolean isEditing() {
-               return xaResource.wc() != null;
-       }
-
-       protected UserDirectoryWorkingCopy getWorkingCopy() {
-               UserDirectoryWorkingCopy wc = xaResource.wc();
-               if (wc == null)
-                       return null;
-               return wc;
-       }
-
-       protected void checkEdit() {
-               Transaction transaction;
-               try {
-                       transaction = transactionManager.getTransaction();
-               } catch (SystemException e) {
-                       throw new UserDirectoryException("Cannot get transaction", e);
-               }
-               if (transaction == null)
-                       throw new UserDirectoryException("A transaction needs to be active in order to edit");
-               if (xaResource.wc() == null) {
-                       try {
-                               transaction.enlistResource(xaResource);
-                       } catch (Exception e) {
-                               throw new UserDirectoryException("Cannot enlist " + xaResource, e);
-                       }
-               } else {
-               }
-       }
-
-       protected List<Role> getAllRoles(DirectoryUser user) {
-               List<Role> allRoles = new ArrayList<Role>();
-               if (user != null) {
-                       collectRoles(user, allRoles);
-                       allRoles.add(user);
-               } else
-                       collectAnonymousRoles(allRoles);
-               return allRoles;
-       }
-
-       private void collectRoles(DirectoryUser user, List<Role> allRoles) {
-               for (LdapName groupDn : getDirectGroups(user.getDn())) {
-                       // TODO check for loops
-                       DirectoryUser group = doGetRole(groupDn);
-                       allRoles.add(group);
-                       collectRoles(group, allRoles);
-               }
-       }
-
-       private void collectAnonymousRoles(List<Role> allRoles) {
-               // TODO gather anonymous roles
-       }
-
-       // USER ADMIN
-       @Override
-       public Role getRole(String name) {
-               return doGetRole(toDn(name));
-       }
-
-       protected DirectoryUser doGetRole(LdapName dn) {
-               UserDirectoryWorkingCopy wc = getWorkingCopy();
-               DirectoryUser user = daoGetRole(dn);
-               if (wc != null) {
-                       if (user == null && wc.getNewUsers().containsKey(dn))
-                               user = wc.getNewUsers().get(dn);
-                       else if (wc.getDeletedUsers().containsKey(dn))
-                               user = null;
-               }
-               return user;
-       }
-
-       @SuppressWarnings("unchecked")
-       @Override
-       public Role[] getRoles(String filter) throws InvalidSyntaxException {
-               UserDirectoryWorkingCopy wc = getWorkingCopy();
-               Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null;
-               List<DirectoryUser> res = doGetRoles(f);
-               if (wc != null) {
-                       for (Iterator<DirectoryUser> it = res.iterator(); it.hasNext();) {
-                               DirectoryUser user = it.next();
-                               LdapName dn = user.getDn();
-                               if (wc.getDeletedUsers().containsKey(dn))
-                                       it.remove();
-                       }
-                       for (DirectoryUser user : wc.getNewUsers().values()) {
-                               if (f == null || f.match(user.getProperties()))
-                                       res.add(user);
-                       }
-                       // no need to check modified users,
-                       // since doGetRoles was already based on the modified attributes
-               }
-               return res.toArray(new Role[res.size()]);
-       }
-
-       @Override
-       public User getUser(String key, String value) {
-               // TODO check value null or empty
-               List<DirectoryUser> collectedUsers = new ArrayList<DirectoryUser>(getIndexedUserProperties().size());
-               if (key != null) {
-                       doGetUser(key, value, collectedUsers);
-               } else {
-                       // try dn
-                       DirectoryUser user = null;
-                       try {
-                               user = (DirectoryUser) getRole(value);
-                               if (user != null)
-                                       collectedUsers.add(user);
-                       } catch (Exception e) {
-                               // silent
-                       }
-                       // try all indexes
-                       for (String attr : getIndexedUserProperties())
-                               doGetUser(attr, value, collectedUsers);
-               }
-               if (collectedUsers.size() == 1)
-                       return collectedUsers.get(0);
-               else if (collectedUsers.size() > 1)
-                       log.warn(collectedUsers.size() + " users for " + (key != null ? key + "=" : "") + value);
-               return null;
-       }
-
-       protected void doGetUser(String key, String value, List<DirectoryUser> collectedUsers) {
-               try {
-                       Filter f = FrameworkUtil.createFilter("(" + key + "=" + value + ")");
-                       List<DirectoryUser> users = doGetRoles(f);
-                       collectedUsers.addAll(users);
-               } catch (InvalidSyntaxException e) {
-                       throw new UserDirectoryException("Cannot get user with " + key + "=" + value, e);
-               }
-       }
-
-       @Override
-       public Authorization getAuthorization(User user) {
-               return new LdifAuthorization((DirectoryUser) user, getAllRoles((DirectoryUser) user));
-       }
-
-       @Override
-       public Role createRole(String name, int type) {
-               checkEdit();
-               UserDirectoryWorkingCopy wc = getWorkingCopy();
-               LdapName dn = toDn(name);
-               if ((daoHasRole(dn) && !wc.getDeletedUsers().containsKey(dn)) || wc.getNewUsers().containsKey(dn))
-                       throw new UserDirectoryException("Already a role " + name);
-               BasicAttributes attrs = new BasicAttributes(true);
-               // attrs.put(LdifName.dn.name(), dn.toString());
-               Rdn nameRdn = dn.getRdn(dn.size() - 1);
-               // TODO deal with multiple attr RDN
-               attrs.put(nameRdn.getType(), nameRdn.getValue());
-               if (wc.getDeletedUsers().containsKey(dn)) {
-                       wc.getDeletedUsers().remove(dn);
-                       wc.getModifiedUsers().put(dn, attrs);
-               } else {
-                       wc.getModifiedUsers().put(dn, attrs);
-                       DirectoryUser newRole = newRole(dn, type, attrs);
-                       wc.getNewUsers().put(dn, newRole);
-               }
-               return getRole(name);
-       }
-
-       protected DirectoryUser newRole(LdapName dn, int type, Attributes attrs) {
-               LdifUser newRole;
-               BasicAttribute objClass = new BasicAttribute(objectClass.name());
-               if (type == Role.USER) {
-                       String userObjClass = newUserObjectClass(dn);
-                       objClass.add(userObjClass);
-                       if (inetOrgPerson.name().equals(userObjClass)) {
-                               objClass.add(organizationalPerson.name());
-                               objClass.add(person.name());
-                       } else if (organizationalPerson.name().equals(userObjClass)) {
-                               objClass.add(person.name());
-                       }
-                       objClass.add(top.name());
-                       attrs.put(objClass);
-                       newRole = new LdifUser(this, dn, attrs);
-               } else if (type == Role.GROUP) {
-                       String groupObjClass = getGroupObjectClass();
-                       objClass.add(groupObjClass);
-                       // objClass.add(LdifName.extensibleObject.name());
-                       objClass.add(top.name());
-                       attrs.put(objClass);
-                       newRole = new LdifGroup(this, dn, attrs);
-               } else
-                       throw new UserDirectoryException("Unsupported type " + type);
-               return newRole;
-       }
-
-       @Override
-       public boolean removeRole(String name) {
-               checkEdit();
-               UserDirectoryWorkingCopy wc = getWorkingCopy();
-               LdapName dn = toDn(name);
-               boolean actuallyDeleted;
-               if (daoHasRole(dn) || wc.getNewUsers().containsKey(dn)) {
-                       DirectoryUser user = (DirectoryUser) getRole(name);
-                       wc.getDeletedUsers().put(dn, user);
-                       actuallyDeleted = true;
-               } else {// just removing from groups (e.g. system roles)
-                       actuallyDeleted = false;
-               }
-               for (LdapName groupDn : getDirectGroups(dn)) {
-                       DirectoryUser group = doGetRole(groupDn);
-                       group.getAttributes().get(getMemberAttributeId()).remove(dn.toString());
-               }
-               return actuallyDeleted;
-       }
-
-       // TRANSACTION
-       protected void prepare(UserDirectoryWorkingCopy wc) {
-
-       }
-
-       protected void commit(UserDirectoryWorkingCopy wc) {
-
-       }
-
-       protected void rollback(UserDirectoryWorkingCopy wc) {
-
-       }
-
-       // UTILITIES
-       protected LdapName toDn(String name) {
-               try {
-                       return new LdapName(name);
-               } catch (InvalidNameException e) {
-                       throw new UserDirectoryException("Badly formatted name", e);
-               }
-       }
-
-       // GETTERS
-       protected String getMemberAttributeId() {
-               return memberAttributeId;
-       }
-
-       protected List<String> getCredentialAttributeIds() {
-               return credentialAttributeIds;
-       }
-
-       protected URI getUri() {
-               return uri;
-       }
-
-       protected List<String> getIndexedUserProperties() {
-               return indexedUserProperties;
-       }
-
-       protected void setIndexedUserProperties(List<String> indexedUserProperties) {
-               this.indexedUserProperties = indexedUserProperties;
-       }
-
-       private static boolean readOnlyDefault(URI uri) {
-               if (uri == null)
-                       return true;
-               if (uri.getScheme().equals("file")) {
-                       File file = new File(uri);
-                       if (file.exists())
-                               return !file.canWrite();
-                       else
-                               return !file.getParentFile().canWrite();
-               }
-               return true;
-       }
-
-       public boolean isReadOnly() {
-               return readOnly;
-       }
-
-       protected UserAdmin getExternalRoles() {
-               return externalRoles;
-       }
-
-       public LdapName getBaseDn() {
-               // always clone so that the property is not modified by reference
-               return (LdapName) baseDn.clone();
-       }
-
-       /** dn can be null, in that case a default should be returned. */
-       public String getUserObjectClass() {
-               return userObjectClass;
-       }
-
-       public String getUserBase() {
-               return userBase;
-       }
-
-       protected String newUserObjectClass(LdapName dn) {
-               return getUserObjectClass();
-       }
-
-       public String getGroupObjectClass() {
-               return groupObjectClass;
-       }
-
-       public String getGroupBase() {
-               return groupBase;
-       }
-
-       public Dictionary<String, Object> getProperties() {
-               return properties;
-       }
-
-       public void setExternalRoles(UserAdmin externalRoles) {
-               this.externalRoles = externalRoles;
-       }
-
-       public void setTransactionManager(TransactionManager transactionManager) {
-               this.transactionManager = transactionManager;
-       }
-
-       public WcXaResource getXaResource() {
-               return xaResource;
-       }
-
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingAuthorization.java
deleted file mode 100644 (file)
index f270b8d..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import javax.security.auth.x500.X500Principal;
-
-import org.osgi.service.useradmin.Authorization;
-
-class AggregatingAuthorization implements Authorization {
-       private final String name;
-       private final String displayName;
-       private final List<String> systemRoles;
-       private final List<String> roles;
-
-       public AggregatingAuthorization(String name, String displayName,
-                       Collection<String> systemRoles, String[] roles) {
-               this.name = new X500Principal(name).getName();
-               this.displayName = displayName;
-               this.systemRoles = Collections.unmodifiableList(new ArrayList<String>(
-                               systemRoles));
-               this.roles = Collections.unmodifiableList(Arrays.asList(roles));
-       }
-
-       @Override
-       public String getName() {
-               return name;
-       }
-
-       @Override
-       public boolean hasRole(String name) {
-               if (systemRoles.contains(name))
-                       return true;
-               if (roles.contains(name))
-                       return true;
-               return false;
-       }
-
-       @Override
-       public String[] getRoles() {
-               int size = systemRoles.size() + roles.size();
-               List<String> res = new ArrayList<String>(size);
-               res.addAll(systemRoles);
-               res.addAll(roles);
-               return res.toArray(new String[size]);
-       }
-
-       @Override
-       public int hashCode() {
-               if (name == null)
-                       return super.hashCode();
-               return name.hashCode();
-       }
-
-       @Override
-       public boolean equals(Object obj) {
-               if (!(obj instanceof Authorization))
-                       return false;
-               Authorization that = (Authorization) obj;
-               if (name == null)
-                       return that.getName() == null;
-               return name.equals(that.getName());
-       }
-
-       @Override
-       public String toString() {
-               return displayName;
-       }
-
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java
deleted file mode 100644 (file)
index 860c5ef..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.useradmin.Authorization;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-
-/**
- * Aggregates multiple {@link UserDirectory} and integrates them with system
- * roles.
- */
-public class AggregatingUserAdmin implements UserAdmin {
-       private final LdapName systemRolesBaseDn;
-
-       // DAOs
-       private AbstractUserDirectory systemRoles = null;
-       private Map<LdapName, AbstractUserDirectory> businessRoles = new HashMap<LdapName, AbstractUserDirectory>();
-
-       public AggregatingUserAdmin(String systemRolesBaseDn) {
-               try {
-                       this.systemRolesBaseDn = new LdapName(systemRolesBaseDn);
-               } catch (InvalidNameException e) {
-                       throw new UserDirectoryException("Cannot initialize " + AggregatingUserAdmin.class, e);
-               }
-       }
-
-       @Override
-       public Role createRole(String name, int type) {
-               return findUserAdmin(name).createRole(name, type);
-       }
-
-       @Override
-       public boolean removeRole(String name) {
-               boolean actuallyDeleted = findUserAdmin(name).removeRole(name);
-               systemRoles.removeRole(name);
-               return actuallyDeleted;
-       }
-
-       @Override
-       public Role getRole(String name) {
-               return findUserAdmin(name).getRole(name);
-       }
-
-       @Override
-       public Role[] getRoles(String filter) throws InvalidSyntaxException {
-               List<Role> res = new ArrayList<Role>();
-               for (UserAdmin userAdmin : businessRoles.values()) {
-                       res.addAll(Arrays.asList(userAdmin.getRoles(filter)));
-               }
-               res.addAll(Arrays.asList(systemRoles.getRoles(filter)));
-               return res.toArray(new Role[res.size()]);
-       }
-
-       @Override
-       public User getUser(String key, String value) {
-               List<User> res = new ArrayList<User>();
-               for (UserAdmin userAdmin : businessRoles.values()) {
-                       User u = userAdmin.getUser(key, value);
-                       if (u != null)
-                               res.add(u);
-               }
-               // Note: node roles cannot contain users, so it is not searched
-               return res.size() == 1 ? res.get(0) : null;
-       }
-
-       @Override
-       public Authorization getAuthorization(User user) {
-               if (user == null) {// anonymous
-                       return systemRoles.getAuthorization(null);
-               }
-               UserAdmin userAdmin = findUserAdmin(user.getName());
-               Authorization rawAuthorization = userAdmin.getAuthorization(user);
-               // gather system roles
-               Set<String> sysRoles = new HashSet<String>();
-               for (String role : rawAuthorization.getRoles()) {
-                       Authorization auth = systemRoles.getAuthorization((User) userAdmin.getRole(role));
-                       sysRoles.addAll(Arrays.asList(auth.getRoles()));
-               }
-               Authorization authorization = new AggregatingAuthorization(rawAuthorization.getName(),
-                               rawAuthorization.toString(), sysRoles, rawAuthorization.getRoles());
-               return authorization;
-       }
-
-       //
-       // USER ADMIN AGGREGATOR
-       //
-       protected void addUserDirectory(AbstractUserDirectory userDirectory) {
-               LdapName baseDn = userDirectory.getBaseDn();
-               if (isSystemRolesBaseDn(baseDn)) {
-                       this.systemRoles = userDirectory;
-                       systemRoles.setExternalRoles(this);
-               } else {
-                       if (businessRoles.containsKey(baseDn))
-                               throw new UserDirectoryException("There is already a user admin for " + baseDn);
-                       businessRoles.put(baseDn, userDirectory);
-               }
-               userDirectory.init();
-               postAdd(userDirectory);
-       }
-
-       /** Called after a new user directory has been added */
-       protected void postAdd(AbstractUserDirectory userDirectory) {
-       }
-
-       private UserAdmin findUserAdmin(String name) {
-               try {
-                       return findUserAdmin(new LdapName(name));
-               } catch (InvalidNameException e) {
-                       throw new UserDirectoryException("Badly formatted name " + name, e);
-               }
-       }
-
-       private UserAdmin findUserAdmin(LdapName name) {
-               if (name.startsWith(systemRolesBaseDn))
-                       return systemRoles;
-               List<UserAdmin> res = new ArrayList<UserAdmin>(1);
-               for (LdapName baseDn : businessRoles.keySet()) {
-                       if (name.startsWith(baseDn))
-                               res.add(businessRoles.get(baseDn));
-               }
-               if (res.size() == 0)
-                       throw new UserDirectoryException("Cannot find user admin for " + name);
-               if (res.size() > 1)
-                       throw new UserDirectoryException("Multiple user admin found for " + name);
-               return res.get(0);
-       }
-
-       protected boolean isSystemRolesBaseDn(LdapName baseDn) {
-               return baseDn.equals(systemRolesBaseDn);
-       }
-
-       protected Dictionary<String, Object> currentState() {
-               Dictionary<String, Object> res = new Hashtable<String, Object>();
-               // res.put(NodeConstants.CN, NodeConstants.DEFAULT);
-               for (LdapName name : businessRoles.keySet()) {
-                       AbstractUserDirectory userDirectory = businessRoles.get(name);
-                       String uri = UserAdminConf.propertiesAsUri(userDirectory.getProperties()).toString();
-                       res.put(uri, "");
-               }
-               return res;
-       }
-
-       public void destroy() {
-               for (LdapName name : businessRoles.keySet()) {
-                       AbstractUserDirectory userDirectory = businessRoles.get(name);
-                       destroy(userDirectory);
-               }
-               businessRoles.clear();
-               businessRoles = null;
-               destroy(systemRoles);
-               systemRoles = null;
-       }
-
-       private void destroy(AbstractUserDirectory userDirectory) {
-               preDestroy(userDirectory);
-               userDirectory.destroy();
-       }
-
-       protected void removeUserDirectory(LdapName baseDn) {
-               if (isSystemRolesBaseDn(baseDn))
-                       throw new UserDirectoryException("System roles cannot be removed ");
-               if (!businessRoles.containsKey(baseDn))
-                       throw new UserDirectoryException("No user directory registered for " + baseDn);
-               AbstractUserDirectory userDirectory = businessRoles.remove(baseDn);
-               destroy(userDirectory);
-       }
-
-       /**
-        * Called before each user directory is destroyed, so that additional
-        * actions can be performed.
-        */
-       protected void preDestroy(UserDirectory userDirectory) {
-       }
-
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/DigestUtils.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/DigestUtils.java
deleted file mode 100644 (file)
index d8f8ce9..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import java.security.MessageDigest;
-
-class DigestUtils {
-       static byte[] sha1(byte[] bytes) {
-               try {
-                       MessageDigest digest = MessageDigest.getInstance("SHA1");
-                       digest.update(bytes);
-                       byte[] checksum = digest.digest();
-                       return checksum;
-               } catch (Exception e) {
-                       throw new UserDirectoryException("Cannot SHA1 digest", e);
-               }
-       }
-
-       private DigestUtils() {
-       }
-
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/DirectoryGroup.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/DirectoryGroup.java
deleted file mode 100644 (file)
index 7f80463..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import java.util.List;
-
-import javax.naming.ldap.LdapName;
-
-import org.osgi.service.useradmin.Group;
-
-/** A group in a user directroy. */
-interface DirectoryGroup extends Group, DirectoryUser {
-       List<LdapName> getMemberNames();
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/DirectoryUser.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/DirectoryUser.java
deleted file mode 100644 (file)
index 146b805..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import javax.naming.directory.Attributes;
-import javax.naming.ldap.LdapName;
-
-import org.osgi.service.useradmin.User;
-
-/** A user in a user directory. */
-interface DirectoryUser extends User {
-       LdapName getDn();
-
-       Attributes getAttributes();
-
-       void publishAttributes(Attributes modifiedAttributes);
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java
deleted file mode 100644 (file)
index 7a617df..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import static org.argeo.osgi.useradmin.LdifName.objectClass;
-
-import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.List;
-
-import javax.naming.Binding;
-import javax.naming.Context;
-import javax.naming.InvalidNameException;
-import javax.naming.NameNotFoundException;
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.SearchControls;
-import javax.naming.directory.SearchResult;
-import javax.naming.ldap.InitialLdapContext;
-import javax.naming.ldap.LdapName;
-import javax.transaction.TransactionManager;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.osgi.framework.Filter;
-
-/**
- * A user admin based on a LDAP server. Requires a {@link TransactionManager}
- * and an open transaction for write access.
- */
-public class LdapUserAdmin extends AbstractUserDirectory {
-       private final static Log log = LogFactory.getLog(LdapUserAdmin.class);
-
-       private InitialLdapContext initialLdapContext = null;
-
-       public LdapUserAdmin(Dictionary<String, ?> properties) {
-               super(properties);
-               try {
-                       Hashtable<String, Object> connEnv = new Hashtable<String, Object>();
-                       connEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
-                       connEnv.put(Context.PROVIDER_URL, getUri().toString());
-                       connEnv.put("java.naming.ldap.attributes.binary", LdifName.userPassword.name());
-
-                       initialLdapContext = new InitialLdapContext(connEnv, null);
-                       // StartTlsResponse tls = (StartTlsResponse) ctx
-                       // .extendedOperation(new StartTlsRequest());
-                       // tls.negotiate();
-                       initialLdapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
-                       Object principal = properties.get(Context.SECURITY_PRINCIPAL);
-                       if (principal != null) {
-                               initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, principal.toString());
-                               Object creds = properties.get(Context.SECURITY_CREDENTIALS);
-                               if (creds != null) {
-                                       initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, creds.toString());
-
-                               }
-                       }
-                       // initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL,
-                       // "uid=admin,ou=system");
-                       // initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS,
-                       // "secret");
-               } catch (Exception e) {
-                       throw new UserDirectoryException("Cannot connect to LDAP", e);
-               }
-       }
-
-       public void destroy() {
-               try {
-                       // tls.close();
-                       initialLdapContext.close();
-               } catch (NamingException e) {
-                       log.error("Cannot destroy LDAP user admin", e);
-               }
-       }
-
-       protected InitialLdapContext getLdapContext() {
-               return initialLdapContext;
-       }
-
-       @Override
-       protected Boolean daoHasRole(LdapName dn) {
-               return daoGetRole(dn) != null;
-       }
-
-       @Override
-       protected DirectoryUser daoGetRole(LdapName name) {
-               try {
-                       Attributes attrs = getLdapContext().getAttributes(name);
-                       if (attrs.size() == 0)
-                               return null;
-                       LdifUser res;
-                       if (attrs.get(objectClass.name()).contains(getGroupObjectClass()))
-                               res = new LdifGroup(this, name, attrs);
-                       else if (attrs.get(objectClass.name()).contains(getUserObjectClass()))
-                               res = new LdifUser(this, name, attrs);
-                       else
-                               throw new UserDirectoryException("Unsupported LDAP type for " + name);
-                       return res;
-               } catch (NamingException e) {
-                       return null;
-               }
-       }
-
-       @Override
-       protected List<DirectoryUser> doGetRoles(Filter f) {
-               try {
-                       String searchFilter = f != null ? f.toString()
-                                       : "(|(" + objectClass + "=" + getUserObjectClass() + ")(" + objectClass + "="
-                                                       + getGroupObjectClass() + "))";
-                       SearchControls searchControls = new SearchControls();
-                       searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
-
-                       LdapName searchBase = getBaseDn();
-                       NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
-
-                       ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
-                       results: while (results.hasMoreElements()) {
-                               SearchResult searchResult = results.next();
-                               Attributes attrs = searchResult.getAttributes();
-                               Attribute objectClassAttr = attrs.get(objectClass.name());
-                               LdapName dn = toDn(searchBase, searchResult);
-                               LdifUser role;
-                               if (objectClassAttr.contains(getGroupObjectClass()))
-                                       role = new LdifGroup(this, dn, attrs);
-                               else if (objectClassAttr.contains(getUserObjectClass()))
-                                       role = new LdifUser(this, dn, attrs);
-                               else {
-                                       log.warn("Unsupported LDAP type for " + searchResult.getName());
-                                       continue results;
-                               }
-                               res.add(role);
-                       }
-                       return res;
-               } catch (Exception e) {
-                       throw new UserDirectoryException("Cannot get roles for filter " + f, e);
-               }
-       }
-
-       private LdapName toDn(LdapName baseDn, Binding binding) throws InvalidNameException {
-               return new LdapName(binding.isRelative() ? binding.getName() + "," + baseDn : binding.getName());
-       }
-
-       @Override
-       protected List<LdapName> getDirectGroups(LdapName dn) {
-               List<LdapName> directGroups = new ArrayList<LdapName>();
-               try {
-                       String searchFilter = "(&(" + objectClass + "=" + getGroupObjectClass() + ")(" + getMemberAttributeId()
-                                       + "=" + dn + "))";
-
-                       SearchControls searchControls = new SearchControls();
-                       searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
-
-                       LdapName searchBase = getBaseDn();
-                       NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
-
-                       while (results.hasMoreElements()) {
-                               SearchResult searchResult = (SearchResult) results.nextElement();
-                               directGroups.add(toDn(searchBase, searchResult));
-                       }
-                       return directGroups;
-               } catch (Exception e) {
-                       throw new UserDirectoryException("Cannot populate direct members of " + dn, e);
-               }
-       }
-
-       @Override
-       protected void prepare(UserDirectoryWorkingCopy wc) {
-               try {
-                       getLdapContext().reconnect(getLdapContext().getConnectControls());
-                       // delete
-                       for (LdapName dn : wc.getDeletedUsers().keySet()) {
-                               if (!entryExists(dn))
-                                       throw new UserDirectoryException("User to delete no found " + dn);
-                       }
-                       // add
-                       for (LdapName dn : wc.getNewUsers().keySet()) {
-                               if (entryExists(dn))
-                                       throw new UserDirectoryException("User to create found " + dn);
-                       }
-                       // modify
-                       for (LdapName dn : wc.getModifiedUsers().keySet()) {
-                               if (!wc.getNewUsers().containsKey(dn) && !entryExists(dn))
-                                       throw new UserDirectoryException("User to modify not found " + dn);
-                       }
-               } catch (NamingException e) {
-                       throw new UserDirectoryException("Cannot prepare LDAP", e);
-               }
-       }
-
-       private boolean entryExists(LdapName dn) throws NamingException {
-               try {
-                       return getLdapContext().getAttributes(dn).size() != 0;
-               } catch (NameNotFoundException e) {
-                       return false;
-               }
-       }
-
-       @Override
-       protected void commit(UserDirectoryWorkingCopy wc) {
-               try {
-                       // delete
-                       for (LdapName dn : wc.getDeletedUsers().keySet()) {
-                               getLdapContext().destroySubcontext(dn);
-                       }
-                       // add
-                       for (LdapName dn : wc.getNewUsers().keySet()) {
-                               DirectoryUser user = wc.getNewUsers().get(dn);
-                               getLdapContext().createSubcontext(dn, user.getAttributes());
-                       }
-                       // modify
-                       for (LdapName dn : wc.getModifiedUsers().keySet()) {
-                               Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
-                               getLdapContext().modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, modifiedAttrs);
-                       }
-               } catch (NamingException e) {
-                       throw new UserDirectoryException("Cannot commit LDAP", e);
-               }
-       }
-
-       @Override
-       protected void rollback(UserDirectoryWorkingCopy wc) {
-               // prepare not impacting
-       }
-
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifAuthorization.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifAuthorization.java
deleted file mode 100644 (file)
index e06c42e..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.List;
-
-import org.osgi.service.useradmin.Authorization;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-
-/** Basic authorization. */
-class LdifAuthorization implements Authorization {
-       private final String name;
-       private final String displayName;
-       private final List<String> allRoles;
-
-       @SuppressWarnings("unchecked")
-       public LdifAuthorization(User user, List<Role> allRoles) {
-               if (user == null) {
-                       this.name = null;
-                       this.displayName = "anonymous";
-               } else {
-                       this.name = user.getName();
-                       Dictionary<String, Object> props = user.getProperties();
-                       Object displayName = props.get(LdifName.displayName);
-                       if (displayName == null)
-                               displayName = props.get(LdifName.cn);
-                       if (displayName == null)
-                               displayName = props.get(LdifName.uid);
-                       if (displayName == null)
-                               displayName = user.getName();
-                       if (displayName == null)
-                               throw new UserDirectoryException("Cannot set display name for "
-                                               + user);
-                       this.displayName = displayName.toString();
-               }
-               // roles
-               String[] roles = new String[allRoles.size()];
-               for (int i = 0; i < allRoles.size(); i++) {
-                       roles[i] = allRoles.get(i).getName();
-               }
-               this.allRoles = Collections.unmodifiableList(Arrays.asList(roles));
-       }
-
-       @Override
-       public String getName() {
-               return name;
-       }
-
-       @Override
-       public boolean hasRole(String name) {
-               return allRoles.contains(name);
-       }
-
-       @Override
-       public String[] getRoles() {
-               return allRoles.toArray(new String[allRoles.size()]);
-       }
-
-       @Override
-       public int hashCode() {
-               if (name == null)
-                       return super.hashCode();
-               return name.hashCode();
-       }
-
-       @Override
-       public boolean equals(Object obj) {
-               if (!(obj instanceof Authorization))
-                       return false;
-               Authorization that = (Authorization) obj;
-               if (name == null)
-                       return that.getName() == null;
-               return name.equals(that.getName());
-       }
-
-       @Override
-       public String toString() {
-               return displayName;
-       }
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifGroup.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifGroup.java
deleted file mode 100644 (file)
index bd12911..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.naming.NamingEnumeration;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.ldap.LdapName;
-
-import org.osgi.service.useradmin.Role;
-
-/** Directory group implementation */
-class LdifGroup extends LdifUser implements DirectoryGroup {
-       private final String memberAttributeId;
-
-       LdifGroup(AbstractUserDirectory userAdmin, LdapName dn,
-                       Attributes attributes) {
-               super(userAdmin, dn, attributes);
-               memberAttributeId = userAdmin.getMemberAttributeId();
-       }
-
-       @Override
-       public boolean addMember(Role role) {
-               getUserAdmin().checkEdit();
-               if (!isEditing())
-                       startEditing();
-
-               Attribute member = getAttributes().get(memberAttributeId);
-               if (member != null) {
-                       if (member.contains(role.getName()))
-                               return false;
-                       else
-                               member.add(role.getName());
-               } else
-                       getAttributes().put(memberAttributeId, role.getName());
-               return true;
-       }
-
-       @Override
-       public boolean addRequiredMember(Role role) {
-               throw new UnsupportedOperationException();
-       }
-
-       @Override
-       public boolean removeMember(Role role) {
-               getUserAdmin().checkEdit();
-               if (!isEditing())
-                       startEditing();
-
-               Attribute member = getAttributes().get(memberAttributeId);
-               if (member != null) {
-                       if (!member.contains(role.getName()))
-                               return false;
-                       member.remove(role.getName());
-                       return true;
-               } else
-                       return false;
-       }
-
-       @Override
-       public Role[] getMembers() {
-               List<Role> directMembers = new ArrayList<Role>();
-               for (LdapName ldapName : getMemberNames()) {
-                       Role role = getUserAdmin().getRole(ldapName.toString());
-                       if (role == null) {
-                               if (getUserAdmin().getExternalRoles() != null)
-                                       role = getUserAdmin().getExternalRoles().getRole(
-                                                       ldapName.toString());
-                       }
-                       if (role == null)
-                               throw new UserDirectoryException("No role found for "
-                                               + ldapName);
-                       directMembers.add(role);
-               }
-               return directMembers.toArray(new Role[directMembers.size()]);
-       }
-
-       @Override
-       public List<LdapName> getMemberNames() {
-               Attribute memberAttribute = getAttributes().get(memberAttributeId);
-               if (memberAttribute == null)
-                       return new ArrayList<LdapName>();
-               try {
-                       List<LdapName> roles = new ArrayList<LdapName>();
-                       NamingEnumeration<?> values = memberAttribute.getAll();
-                       while (values.hasMore()) {
-                               LdapName dn = new LdapName(values.next().toString());
-                               roles.add(dn);
-                       }
-                       return roles;
-               } catch (Exception e) {
-                       throw new UserDirectoryException("Cannot get members", e);
-               }
-       }
-
-       @Override
-       public Role[] getRequiredMembers() {
-               throw new UnsupportedOperationException();
-       }
-
-       @Override
-       public int getType() {
-               return GROUP;
-       }
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifName.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifName.java
deleted file mode 100644 (file)
index 919e507..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import javax.naming.ldap.LdapName;
-
-/**
- * Standard LDAP attributes and object classes leveraged in this implementation
- * of user admin. Named {@link LdifName} in order not to collide with
- * {@link LdapName}.
- */
-public enum LdifName {
-       // Attributes
-       dn, dc, cn, sn, uid, mail, displayName, objectClass, userPassword, givenName, description, member,
-       // Object classes
-       inetOrgPerson, organizationalPerson, person, groupOfNames, groupOfUniqueNames, top;
-
-       public final static String PREFIX = "ldap:";
-
-       /** For use as XML name. */
-       public String property() {
-               return PREFIX + name();
-       }
-
-       public static LdifName local(String property) {
-               return LdifName.valueOf(property.substring(PREFIX.length()));
-       }
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java
deleted file mode 100644 (file)
index 866c48c..0000000
+++ /dev/null
@@ -1,363 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.BasicAttribute;
-import javax.naming.ldap.LdapName;
-
-/** Directory user implementation */
-class LdifUser implements DirectoryUser {
-       private final AbstractUserDirectory userAdmin;
-
-       private final LdapName dn;
-
-       private final boolean frozen;
-       private Attributes publishedAttributes;
-
-       private final AttributeDictionary properties;
-       private final AttributeDictionary credentials;
-
-       LdifUser(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes) {
-               this(userAdmin, dn, attributes, false);
-       }
-
-       private LdifUser(AbstractUserDirectory userAdmin, LdapName dn, Attributes attributes, boolean frozen) {
-               this.userAdmin = userAdmin;
-               this.dn = dn;
-               this.publishedAttributes = attributes;
-               properties = new AttributeDictionary(false);
-               credentials = new AttributeDictionary(true);
-               this.frozen = frozen;
-       }
-
-       @Override
-       public String getName() {
-               return dn.toString();
-       }
-
-       @Override
-       public int getType() {
-               return USER;
-       }
-
-       @Override
-       public Dictionary<String, Object> getProperties() {
-               return properties;
-       }
-
-       @Override
-       public Dictionary<String, Object> getCredentials() {
-               return credentials;
-       }
-
-       @Override
-       public boolean hasCredential(String key, Object value) {
-               if (key == null) {
-                       // TODO check other sources (like PKCS12)
-                       char[] password = toChars(value);
-                       byte[] hashedPassword = hash(password);
-                       return hasCredential(LdifName.userPassword.name(), hashedPassword);
-               }
-
-               Object storedValue = getCredentials().get(key);
-               if (storedValue == null || value == null)
-                       return false;
-               if (!(value instanceof String || value instanceof byte[]))
-                       return false;
-               if (storedValue instanceof String && value instanceof String)
-                       return storedValue.equals(value);
-               if (storedValue instanceof byte[] && value instanceof byte[])
-                       return Arrays.equals((byte[]) storedValue, (byte[]) value);
-               return false;
-       }
-
-       /** Hash and clear the password */
-       private byte[] hash(char[] password) {
-               byte[] hashedPassword = ("{SHA}" + Base64.getEncoder().encodeToString(DigestUtils.sha1(toBytes(password))))
-                               .getBytes();
-               Arrays.fill(password, '\u0000');
-               return hashedPassword;
-       }
-
-       private byte[] toBytes(char[] chars) {
-               CharBuffer charBuffer = CharBuffer.wrap(chars);
-               ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
-               byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
-               Arrays.fill(charBuffer.array(), '\u0000'); // clear sensitive data
-               Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data
-               return bytes;
-       }
-
-       private char[] toChars(Object obj) {
-               if (obj instanceof char[])
-                       return (char[]) obj;
-               if (!(obj instanceof byte[]))
-                       throw new IllegalArgumentException(obj.getClass() + " is not a byte array");
-               ByteBuffer fromBuffer = ByteBuffer.wrap((byte[]) obj);
-               CharBuffer toBuffer = Charset.forName("UTF-8").decode(fromBuffer);
-               char[] res = Arrays.copyOfRange(toBuffer.array(), toBuffer.position(), toBuffer.limit());
-               Arrays.fill(fromBuffer.array(), (byte) 0); // clear sensitive data
-               Arrays.fill((byte[]) obj, (byte) 0); // clear sensitive data
-               Arrays.fill(toBuffer.array(), '\u0000'); // clear sensitive data
-               return res;
-       }
-
-       @Override
-       public LdapName getDn() {
-               return dn;
-       }
-
-       @Override
-       public synchronized Attributes getAttributes() {
-               return isEditing() ? getModifiedAttributes() : publishedAttributes;
-       }
-
-       /** Should only be called from working copy thread. */
-       private synchronized Attributes getModifiedAttributes() {
-               assert getWc() != null;
-               return getWc().getAttributes(getDn());
-       }
-
-       protected synchronized boolean isEditing() {
-               return getWc() != null && getModifiedAttributes() != null;
-       }
-
-       private synchronized UserDirectoryWorkingCopy getWc() {
-               return userAdmin.getWorkingCopy();
-       }
-
-       protected synchronized void startEditing() {
-               if (frozen)
-                       throw new UserDirectoryException("Cannot edit frozen view");
-               if (getUserAdmin().isReadOnly())
-                       throw new UserDirectoryException("User directory is read-only");
-               assert getModifiedAttributes() == null;
-               getWc().startEditing(this);
-               // modifiedAttributes = (Attributes) publishedAttributes.clone();
-       }
-
-       public synchronized void publishAttributes(Attributes modifiedAttributes) {
-               publishedAttributes = modifiedAttributes;
-       }
-
-       public DirectoryUser getPublished() {
-               return new LdifUser(userAdmin, dn, publishedAttributes, true);
-       }
-
-       @Override
-       public int hashCode() {
-               return dn.hashCode();
-       }
-
-       @Override
-       public boolean equals(Object obj) {
-               if (this == obj)
-                       return true;
-               if (obj instanceof LdifUser) {
-                       LdifUser that = (LdifUser) obj;
-                       return this.dn.equals(that.dn);
-               }
-               return false;
-       }
-
-       @Override
-       public String toString() {
-               return dn.toString();
-       }
-
-       protected AbstractUserDirectory getUserAdmin() {
-               return userAdmin;
-       }
-
-       private class AttributeDictionary extends Dictionary<String, Object> {
-               private final List<String> effectiveKeys = new ArrayList<String>();
-               private final List<String> attrFilter;
-               private final Boolean includeFilter;
-
-               public AttributeDictionary(Boolean includeFilter) {
-                       this.attrFilter = userAdmin.getCredentialAttributeIds();
-                       this.includeFilter = includeFilter;
-                       try {
-                               NamingEnumeration<String> ids = getAttributes().getIDs();
-                               while (ids.hasMore()) {
-                                       String id = ids.next();
-                                       if (includeFilter && attrFilter.contains(id))
-                                               effectiveKeys.add(id);
-                                       else if (!includeFilter && !attrFilter.contains(id))
-                                               effectiveKeys.add(id);
-                               }
-                       } catch (NamingException e) {
-                               throw new UserDirectoryException("Cannot initialise attribute dictionary", e);
-                       }
-               }
-
-               @Override
-               public int size() {
-                       return effectiveKeys.size();
-               }
-
-               @Override
-               public boolean isEmpty() {
-                       return effectiveKeys.size() == 0;
-               }
-
-               @Override
-               public Enumeration<String> keys() {
-                       return Collections.enumeration(effectiveKeys);
-               }
-
-               @Override
-               public Enumeration<Object> elements() {
-                       final Iterator<String> it = effectiveKeys.iterator();
-                       return new Enumeration<Object>() {
-
-                               @Override
-                               public boolean hasMoreElements() {
-                                       return it.hasNext();
-                               }
-
-                               @Override
-                               public Object nextElement() {
-                                       String key = it.next();
-                                       return get(key);
-                               }
-
-                       };
-               }
-
-               @Override
-               public Object get(Object key) {
-                       try {
-                               Attribute attr = getAttributes().get(key.toString());
-                               if (attr == null)
-                                       return null;
-                               Object value = attr.get();
-                               if (value instanceof byte[]) {
-                                       if (key.equals(LdifName.userPassword.name()))
-                                               // TODO other cases (certificates, images)
-                                               return value;
-                                       value = new String((byte[]) value, Charset.forName("UTF-8"));
-                               }
-                               if (attr.size() == 1)
-                                       return value;
-                               if (!attr.getID().equals(LdifName.objectClass.name()))
-                                       return value;
-                               // special case for object class
-                               NamingEnumeration<?> en = attr.getAll();
-                               Set<String> objectClasses = new HashSet<String>();
-                               while (en.hasMore()) {
-                                       String objectClass = en.next().toString();
-                                       objectClasses.add(objectClass);
-                               }
-
-                               if (objectClasses.contains(userAdmin.getUserObjectClass()))
-                                       return userAdmin.getUserObjectClass();
-                               else if (objectClasses.contains(userAdmin.getGroupObjectClass()))
-                                       return userAdmin.getGroupObjectClass();
-                               else
-                                       return value;
-                       } catch (NamingException e) {
-                               throw new UserDirectoryException("Cannot get value for attribute " + key, e);
-                       }
-               }
-
-               @Override
-               public Object put(String key, Object value) {
-                       if (key == null) {
-                               // TODO persist to other sources (like PKCS12)
-                               char[] password = toChars(value);
-                               byte[] hashedPassword = hash(password);
-                               return put(LdifName.userPassword.name(), hashedPassword);
-                       }
-
-                       userAdmin.checkEdit();
-                       if (!isEditing())
-                               startEditing();
-
-                       if (!(value instanceof String || value instanceof byte[]))
-                               throw new IllegalArgumentException("Value must be String or byte[]");
-
-                       if (includeFilter && !attrFilter.contains(key))
-                               throw new IllegalArgumentException("Key " + key + " not included");
-                       else if (!includeFilter && attrFilter.contains(key))
-                               throw new IllegalArgumentException("Key " + key + " excluded");
-
-                       try {
-                               Attribute attribute = getModifiedAttributes().get(key.toString());
-                               attribute = new BasicAttribute(key.toString());
-                               if (value instanceof String && !isAsciiPrintable(((String) value)))
-                                       try {
-                                               attribute.add(((String) value).getBytes("UTF-8"));
-                                       } catch (UnsupportedEncodingException e) {
-                                               throw new UserDirectoryException("Cannot encode " + value, e);
-                                       }
-                               else
-                                       attribute.add(value);
-                               Attribute previousAttribute = getModifiedAttributes().put(attribute);
-                               if (previousAttribute != null)
-                                       return previousAttribute.get();
-                               else
-                                       return null;
-                       } catch (NamingException e) {
-                               throw new UserDirectoryException("Cannot get value for attribute " + key, e);
-                       }
-               }
-
-               @Override
-               public Object remove(Object key) {
-                       userAdmin.checkEdit();
-                       if (!isEditing())
-                               startEditing();
-
-                       if (includeFilter && !attrFilter.contains(key))
-                               throw new IllegalArgumentException("Key " + key + " not included");
-                       else if (!includeFilter && attrFilter.contains(key))
-                               throw new IllegalArgumentException("Key " + key + " excluded");
-
-                       try {
-                               Attribute attr = getModifiedAttributes().remove(key.toString());
-                               if (attr != null)
-                                       return attr.get();
-                               else
-                                       return null;
-                       } catch (NamingException e) {
-                               throw new UserDirectoryException("Cannot remove attribute " + key, e);
-                       }
-               }
-       }
-
-       private static boolean isAsciiPrintable(String str) {
-               if (str == null) {
-                       return false;
-               }
-               int sz = str.length();
-               for (int i = 0; i < sz; i++) {
-                       if (isAsciiPrintable(str.charAt(i)) == false) {
-                               return false;
-                       }
-               }
-               return true;
-       }
-
-       private static boolean isAsciiPrintable(char ch) {
-               return ch >= 32 && ch < 127;
-       }
-
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java
deleted file mode 100644 (file)
index 521ae8b..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import static org.argeo.osgi.useradmin.LdifName.inetOrgPerson;
-import static org.argeo.osgi.useradmin.LdifName.objectClass;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import javax.naming.NamingEnumeration;
-import javax.naming.directory.Attributes;
-import javax.naming.ldap.LdapName;
-import javax.transaction.TransactionManager;
-
-import org.argeo.util.naming.LdifParser;
-import org.argeo.util.naming.LdifWriter;
-import org.osgi.framework.Filter;
-import org.osgi.service.useradmin.Role;
-
-/**
- * A user admin based on a LDIF files. Requires a {@link TransactionManager} and
- * an open transaction for write access.
- */
-public class LdifUserAdmin extends AbstractUserDirectory {
-       private SortedMap<LdapName, DirectoryUser> users = new TreeMap<LdapName, DirectoryUser>();
-       private SortedMap<LdapName, DirectoryGroup> groups = new TreeMap<LdapName, DirectoryGroup>();
-
-       public LdifUserAdmin(String uri, String baseDn) {
-               this(fromUri(uri, baseDn));
-       }
-
-       public LdifUserAdmin(Dictionary<String, ?> properties) {
-               super(properties);
-       }
-
-       public LdifUserAdmin(InputStream in) {
-               super(new Hashtable<String, Object>());
-               load(in);
-       }
-
-       private static Dictionary<String, Object> fromUri(String uri, String baseDn) {
-               Hashtable<String, Object> res = new Hashtable<String, Object>();
-               res.put(UserAdminConf.uri.name(), uri);
-               res.put(UserAdminConf.baseDn.name(), baseDn);
-               return res;
-       }
-
-       public void init() {
-               try {
-                       if (getUri().getScheme().equals("file")) {
-                               File file = new File(getUri());
-                               if (!file.exists())
-                                       return;
-                       }
-                       load(getUri().toURL().openStream());
-               } catch (Exception e) {
-                       throw new UserDirectoryException("Cannot open URL " + getUri(), e);
-               }
-       }
-
-       public void save() {
-               if (getUri() == null)
-                       throw new UserDirectoryException("Cannot save LDIF user admin: no URI is set");
-               if (isReadOnly())
-                       throw new UserDirectoryException("Cannot save LDIF user admin: " + getUri() + " is read-only");
-               try (FileOutputStream out = new FileOutputStream(new File(getUri()))) {
-                       save(out);
-               } catch (IOException e) {
-                       throw new UserDirectoryException("Cannot save user admin to " + getUri(), e);
-               }
-       }
-
-       public void save(OutputStream out) throws IOException {
-               try {
-                       LdifWriter ldifWriter = new LdifWriter(out);
-                       for (LdapName name : groups.keySet())
-                               ldifWriter.writeEntry(name, groups.get(name).getAttributes());
-                       for (LdapName name : users.keySet())
-                               ldifWriter.writeEntry(name, users.get(name).getAttributes());
-               } finally {
-                       out.close();
-               }
-       }
-
-       protected void load(InputStream in) {
-               try {
-                       users.clear();
-                       groups.clear();
-
-                       LdifParser ldifParser = new LdifParser();
-                       SortedMap<LdapName, Attributes> allEntries = ldifParser.read(in);
-                       for (LdapName key : allEntries.keySet()) {
-                               Attributes attributes = allEntries.get(key);
-                               // check for inconsistency
-                               Set<String> lowerCase = new HashSet<String>();
-                               NamingEnumeration<String> ids = attributes.getIDs();
-                               while (ids.hasMoreElements()) {
-                                       String id = ids.nextElement().toLowerCase();
-                                       if (lowerCase.contains(id))
-                                               throw new UserDirectoryException(key + " has duplicate id " + id);
-                                       lowerCase.add(id);
-                               }
-
-                               // analyse object classes
-                               NamingEnumeration<?> objectClasses = attributes.get(objectClass.name()).getAll();
-                               // System.out.println(key);
-                               objectClasses: while (objectClasses.hasMore()) {
-                                       String objectClass = objectClasses.next().toString();
-                                       // System.out.println(" " + objectClass);
-                                       if (objectClass.equals(inetOrgPerson.name())) {
-                                               users.put(key, new LdifUser(this, key, attributes));
-                                               break objectClasses;
-                                       } else if (objectClass.equals(getGroupObjectClass())) {
-                                               groups.put(key, new LdifGroup(this, key, attributes));
-                                               break objectClasses;
-                                       }
-                               }
-                       }
-               } catch (Exception e) {
-                       throw new UserDirectoryException("Cannot load user admin service from LDIF", e);
-               }
-       }
-
-       public void destroy() {
-               if (users == null || groups == null)
-                       throw new UserDirectoryException("User directory " + getBaseDn() + " is already destroyed");
-               users.clear();
-               users = null;
-               groups.clear();
-               groups = null;
-       }
-
-       protected DirectoryUser daoGetRole(LdapName key) {
-               if (groups.containsKey(key))
-                       return groups.get(key);
-               if (users.containsKey(key))
-                       return users.get(key);
-               return null;
-       }
-
-       protected Boolean daoHasRole(LdapName dn) {
-               return users.containsKey(dn) || groups.containsKey(dn);
-       }
-
-       @SuppressWarnings("unchecked")
-       protected List<DirectoryUser> doGetRoles(Filter f) {
-               ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
-               if (f == null) {
-                       res.addAll(users.values());
-                       res.addAll(groups.values());
-               } else {
-                       for (DirectoryUser user : users.values()) {
-                               // System.out.println("\n" + user.getName());
-                               // Dictionary<String, Object> props = user.getProperties();
-                               // for (Enumeration<String> keys = props.keys(); keys
-                               // .hasMoreElements();) {
-                               // String key = keys.nextElement();
-                               // System.out.println(" " + key + "=" + props.get(key));
-                               // }
-                               if (f.match(user.getProperties()))
-                                       res.add(user);
-                       }
-                       for (DirectoryUser group : groups.values())
-                               if (f.match(group.getProperties()))
-                                       res.add(group);
-               }
-               return res;
-       }
-
-       @Override
-       protected List<LdapName> getDirectGroups(LdapName dn) {
-               List<LdapName> directGroups = new ArrayList<LdapName>();
-               for (LdapName name : groups.keySet()) {
-                       DirectoryGroup group = groups.get(name);
-                       if (group.getMemberNames().contains(dn))
-                               directGroups.add(group.getDn());
-               }
-               return directGroups;
-       }
-
-       @Override
-       protected void prepare(UserDirectoryWorkingCopy wc) {
-               // delete
-               for (LdapName dn : wc.getDeletedUsers().keySet()) {
-                       if (users.containsKey(dn))
-                               users.remove(dn);
-                       else if (groups.containsKey(dn))
-                               groups.remove(dn);
-                       else
-                               throw new UserDirectoryException("User to delete not found " + dn);
-               }
-               // add
-               for (LdapName dn : wc.getNewUsers().keySet()) {
-                       DirectoryUser user = wc.getNewUsers().get(dn);
-                       if (users.containsKey(dn) || groups.containsKey(dn))
-                               throw new UserDirectoryException("User to create found " + dn);
-                       else if (Role.USER == user.getType())
-                               users.put(dn, user);
-                       else if (Role.GROUP == user.getType())
-                               groups.put(dn, (DirectoryGroup) user);
-                       else
-                               throw new UserDirectoryException("Unsupported role type " + user.getType() + " for new user " + dn);
-               }
-               // modify
-               for (LdapName dn : wc.getModifiedUsers().keySet()) {
-                       Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
-                       DirectoryUser user;
-                       if (users.containsKey(dn))
-                               user = users.get(dn);
-                       else if (groups.containsKey(dn))
-                               user = groups.get(dn);
-                       else
-                               throw new UserDirectoryException("User to modify no found " + dn);
-                       user.publishAttributes(modifiedAttrs);
-               }
-       }
-
-       @Override
-       protected void commit(UserDirectoryWorkingCopy wc) {
-               save();
-       }
-
-       @Override
-       protected void rollback(UserDirectoryWorkingCopy wc) {
-               init();
-       }
-
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserAdminConf.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserAdminConf.java
deleted file mode 100644 (file)
index 316941e..0000000
+++ /dev/null
@@ -1,193 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URLDecoder;
-import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import javax.naming.Context;
-
-import org.osgi.framework.Constants;
-
-/** Properties used to configure user admins. */
-public enum UserAdminConf {
-       /** Base DN (cannot be configured externally) */
-       baseDn("dc=example,dc=com"),
-
-       /** URI of the underlying resource (cannot be configured externally) */
-       uri("ldap://localhost:10389"),
-
-       /** User objectClass */
-       userObjectClass("inetOrgPerson"),
-
-       /** Relative base DN for users */
-       userBase("ou=People"),
-
-       /** Groups objectClass */
-       groupObjectClass("groupOfNames"),
-
-       /** Relative base DN for users */
-       groupBase("ou=Groups"),
-
-       /** Read-only source */
-       readOnly(null);
-
-       public final static String FACTORY_PID = "org.argeo.osgi.useradmin.config";
-
-       /** The default value. */
-       private Object def;
-
-       UserAdminConf(Object def) {
-               this.def = def;
-       }
-
-       public Object getDefault() {
-               return def;
-       }
-
-       /**
-        * For use as Java property.
-        * 
-        * @deprecated use {@link #name()} instead
-        */
-       @Deprecated
-       public String property() {
-               return name();
-       }
-
-       public String getValue(Dictionary<String, ?> properties) {
-               Object res = getRawValue(properties);
-               if (res == null)
-                       return null;
-               return res.toString();
-       }
-
-       @SuppressWarnings("unchecked")
-       public <T> T getRawValue(Dictionary<String, ?> properties) {
-               Object res = properties.get(name());
-               if (res == null)
-                       res = getDefault();
-               return (T) res;
-       }
-
-       /** @deprecated use {@link #valueOf(String)} instead */
-       @Deprecated
-       public static UserAdminConf local(String property) {
-               return UserAdminConf.valueOf(property);
-       }
-
-       /** Hides host and credentials. */
-       public static URI propertiesAsUri(Dictionary<String, ?> properties) {
-               StringBuilder query = new StringBuilder();
-
-               boolean first = true;
-               for (Enumeration<String> keys = properties.keys(); keys.hasMoreElements();) {
-                       String key = keys.nextElement();
-                       // TODO clarify which keys are relevant (list only the enum?)
-                       if (!key.equals("service.factoryPid") && !key.equals("cn") && !key.equals("dn")
-                                       && !key.equals(Constants.SERVICE_PID) && !key.startsWith("java") && !key.equals(baseDn.name())
-                                       && !key.equals(uri.name())) {
-                               if (first)
-                                       first = false;
-                               else
-                                       query.append('&');
-                               query.append(valueOf(key).name());
-                               query.append('=').append(properties.get(key).toString());
-                       }
-               }
-
-               String bDn = (String) properties.get(baseDn.name());
-               try {
-                       return new URI(null, null, bDn != null ? '/' + bDn : null, query.length() != 0 ? query.toString() : null,
-                                       null);
-               } catch (URISyntaxException e) {
-                       throw new UserDirectoryException("Cannot create URI from properties", e);
-               }
-       }
-
-       public static Dictionary<String, Object> uriAsProperties(String uriStr) {
-               try {
-                       Hashtable<String, Object> res = new Hashtable<String, Object>();
-                       URI u = new URI(uriStr);
-                       String scheme = u.getScheme();
-                       String path = u.getPath();
-                       String bDn = path.substring(path.lastIndexOf('/') + 1, path.length());
-                       if (bDn.endsWith(".ldif"))
-                               bDn = bDn.substring(0, bDn.length() - ".ldif".length());
-
-                       String principal = null;
-                       String credentials = null;
-                       if (scheme != null)
-                               if (scheme.equals("ldap") || scheme.equals("ldaps")) {
-                                       // TODO additional checks
-                                       String[] userInfo = u.getUserInfo().split(":");
-                                       principal = userInfo.length > 0 ? userInfo[0] : null;
-                                       credentials = userInfo.length > 1 ? userInfo[1] : null;
-                               } else if (scheme.equals("file")) {
-                               } else
-                                       throw new UserDirectoryException("Unsupported scheme " + scheme);
-                       Map<String, List<String>> query = splitQuery(u.getQuery());
-                       for (String key : query.keySet()) {
-                               UserAdminConf ldapProp = UserAdminConf.valueOf(key);
-                               List<String> values = query.get(key);
-                               if (values.size() == 1) {
-                                       res.put(ldapProp.name(), values.get(0));
-                               } else {
-                                       throw new UserDirectoryException("Only single values are supported");
-                               }
-                       }
-                       res.put(baseDn.name(), bDn);
-                       if (principal != null)
-                               res.put(Context.SECURITY_PRINCIPAL, principal);
-                       if (credentials != null)
-                               res.put(Context.SECURITY_CREDENTIALS, credentials);
-                       if (scheme != null) {
-                               URI bareUri = new URI(scheme, null, u.getHost(), u.getPort(),
-                                               scheme.equals("file") ? u.getPath() : null, null, null);
-                               res.put(uri.name(), bareUri.toString());
-                       }
-                       return res;
-               } catch (Exception e) {
-                       throw new UserDirectoryException("Cannot convert " + uri + " to properties", e);
-               }
-       }
-
-       private static Map<String, List<String>> splitQuery(String query) throws UnsupportedEncodingException {
-               final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
-               if (query == null)
-                       return query_pairs;
-               final String[] pairs = query.split("&");
-               for (String pair : pairs) {
-                       final int idx = pair.indexOf("=");
-                       final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
-                       if (!query_pairs.containsKey(key)) {
-                               query_pairs.put(key, new LinkedList<String>());
-                       }
-                       final String value = idx > 0 && pair.length() > idx + 1
-                                       ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
-                       query_pairs.get(key).add(value);
-               }
-               return query_pairs;
-       }
-
-       public static void main(String[] args) {
-               Dictionary<String, ?> props = uriAsProperties("ldap://" + "uid=admin,ou=system:secret@localhost:10389"
-                               + "/dc=example,dc=com" + "?readOnly=false&userObjectClass=person");
-               System.out.println(props);
-               System.out.println(propertiesAsUri(props));
-
-               System.out.println(uriAsProperties("file://some/dir/dc=example,dc=com.ldif"));
-
-               props = uriAsProperties(
-                               "/dc=example,dc=com.ldif?readOnly=true" + "&userBase=ou=CoWorkers,ou=People&groupBase=ou=Roles");
-               System.out.println(props);
-               System.out.println(propertiesAsUri(props));
-       }
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java
deleted file mode 100644 (file)
index e5de738..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import javax.naming.ldap.LdapName;
-import javax.transaction.xa.XAResource;
-
-/** Information about a user directory. */
-public interface UserDirectory {
-       /** The base DN of all entries in this user directory */
-       public LdapName getBaseDn();
-
-       /** The related {@link XAResource} */
-       public XAResource getXaResource();
-
-       public boolean isReadOnly();
-
-       public String getUserObjectClass();
-
-       public String getUserBase();
-
-       public String getGroupObjectClass();
-
-       public String getGroupBase();
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryException.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryException.java
deleted file mode 100644 (file)
index 613d0fd..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import org.osgi.service.useradmin.UserAdmin;
-
-/**
- * Exceptions related to Argeo's implementation of OSGi {@link UserAdmin}
- * service.
- */
-public class UserDirectoryException extends RuntimeException {
-       private static final long serialVersionUID = 1419352360062048603L;
-
-       public UserDirectoryException(String message) {
-               super(message);
-       }
-
-       public UserDirectoryException(String message, Throwable e) {
-               super(message, e);
-       }
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java
deleted file mode 100644 (file)
index 0e25bdf..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.naming.directory.Attributes;
-import javax.naming.ldap.LdapName;
-import javax.transaction.xa.XAResource;
-
-/** {@link XAResource} for a user directory being edited. */
-class UserDirectoryWorkingCopy {
-       // private final static Log log = LogFactory
-       // .getLog(UserDirectoryWorkingCopy.class);
-
-       private Map<LdapName, DirectoryUser> newUsers = new HashMap<LdapName, DirectoryUser>();
-       private Map<LdapName, Attributes> modifiedUsers = new HashMap<LdapName, Attributes>();
-       private Map<LdapName, DirectoryUser> deletedUsers = new HashMap<LdapName, DirectoryUser>();
-
-       void cleanUp() {
-               // clean collections
-               newUsers.clear();
-               newUsers = null;
-               modifiedUsers.clear();
-               modifiedUsers = null;
-               deletedUsers.clear();
-               deletedUsers = null;
-       }
-
-       public boolean noModifications() {
-               return newUsers.size() == 0 && modifiedUsers.size() == 0
-                               && deletedUsers.size() == 0;
-       }
-
-       public Attributes getAttributes(LdapName dn) {
-               if (modifiedUsers.containsKey(dn))
-                       return modifiedUsers.get(dn);
-               return null;
-       }
-
-       public void startEditing(DirectoryUser user) {
-               LdapName dn = user.getDn();
-               if (modifiedUsers.containsKey(dn))
-                       throw new UserDirectoryException("Already editing " + dn);
-               modifiedUsers.put(dn, (Attributes) user.getAttributes().clone());
-       }
-
-       public Map<LdapName, DirectoryUser> getNewUsers() {
-               return newUsers;
-       }
-
-       public Map<LdapName, DirectoryUser> getDeletedUsers() {
-               return deletedUsers;
-       }
-
-       public Map<LdapName, Attributes> getModifiedUsers() {
-               return modifiedUsers;
-       }
-}
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/WcXaResource.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/WcXaResource.java
deleted file mode 100644 (file)
index a6048fd..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-package org.argeo.osgi.useradmin;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.transaction.xa.XAException;
-import javax.transaction.xa.XAResource;
-import javax.transaction.xa.Xid;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/** {@link XAResource} for a user directory being edited. */
-class WcXaResource implements XAResource {
-       private final static Log log = LogFactory.getLog(WcXaResource.class);
-
-       private final AbstractUserDirectory userDirectory;
-
-       private Map<Xid, UserDirectoryWorkingCopy> workingCopies = new HashMap<Xid, UserDirectoryWorkingCopy>();
-       private Xid editingXid = null;
-       private int transactionTimeout = 0;
-
-       public WcXaResource(AbstractUserDirectory userDirectory) {
-               this.userDirectory = userDirectory;
-       }
-
-       @Override
-       public synchronized void start(Xid xid, int flags) throws XAException {
-               if (editingXid != null)
-                       throw new UserDirectoryException("Already editing " + editingXid);
-               UserDirectoryWorkingCopy wc = workingCopies.put(xid,
-                               new UserDirectoryWorkingCopy());
-               if (wc != null)
-                       throw new UserDirectoryException(
-                                       "There is already a working copy for " + xid);
-               this.editingXid = xid;
-       }
-
-       @Override
-       public void end(Xid xid, int flags) throws XAException {
-               checkXid(xid);
-       }
-
-       private UserDirectoryWorkingCopy wc(Xid xid) {
-               return workingCopies.get(xid);
-       }
-
-       synchronized UserDirectoryWorkingCopy wc() {
-               if (editingXid == null)
-                       return null;
-               UserDirectoryWorkingCopy wc = workingCopies.get(editingXid);
-               if (wc == null)
-                       throw new UserDirectoryException("No working copy found for "
-                                       + editingXid);
-               return wc;
-       }
-
-       private synchronized void cleanUp(Xid xid) {
-               wc(xid).cleanUp();
-               workingCopies.remove(xid);
-               editingXid = null;
-       }
-
-       @Override
-       public int prepare(Xid xid) throws XAException {
-               checkXid(xid);
-               UserDirectoryWorkingCopy wc = wc(xid);
-               if (wc.noModifications())
-                       return XA_RDONLY;
-               try {
-                       userDirectory.prepare(wc);
-               } catch (Exception e) {
-                       log.error("Cannot prepare " + xid, e);
-                       throw new XAException(XAException.XAER_RMERR);
-               }
-               return XA_OK;
-       }
-
-       @Override
-       public void commit(Xid xid, boolean onePhase) throws XAException {
-               try {
-                       checkXid(xid);
-                       UserDirectoryWorkingCopy wc = wc(xid);
-                       if (wc.noModifications())
-                               return;
-                       if (onePhase)
-                               userDirectory.prepare(wc);
-                       userDirectory.commit(wc);
-               } catch (Exception e) {
-                       log.error("Cannot commit " + xid, e);
-                       throw new XAException(XAException.XAER_RMERR);
-               } finally {
-                       cleanUp(xid);
-               }
-       }
-
-       @Override
-       public void rollback(Xid xid) throws XAException {
-               try {
-                       checkXid(xid);
-                       userDirectory.rollback(wc(xid));
-               } catch (Exception e) {
-                       log.error("Cannot rollback " + xid, e);
-                       throw new XAException(XAException.XAER_RMERR);
-               } finally {
-                       cleanUp(xid);
-               }
-       }
-
-       @Override
-       public void forget(Xid xid) throws XAException {
-               throw new UnsupportedOperationException();
-       }
-
-       @Override
-       public boolean isSameRM(XAResource xares) throws XAException {
-               return xares == this;
-       }
-
-       @Override
-       public Xid[] recover(int flag) throws XAException {
-               return new Xid[0];
-       }
-
-       @Override
-       public int getTransactionTimeout() throws XAException {
-               return transactionTimeout;
-       }
-
-       @Override
-       public boolean setTransactionTimeout(int seconds) throws XAException {
-               transactionTimeout = seconds;
-               return true;
-       }
-
-       private void checkXid(Xid xid) throws XAException {
-               if (xid == null)
-                       throw new XAException(XAException.XAER_OUTSIDE);
-               if (!xid.equals(xid))
-                       throw new XAException(XAException.XAER_NOTA);
-       }
-
-}
diff --git a/org.argeo.security.core/src/org/argeo/util/naming/AttributesDictionary.java b/org.argeo.security.core/src/org/argeo/util/naming/AttributesDictionary.java
deleted file mode 100644 (file)
index c211e8e..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-package org.argeo.util.naming;
-
-import java.util.Dictionary;
-import java.util.Enumeration;
-
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.BasicAttribute;
-
-public class AttributesDictionary extends Dictionary<String, Object> {
-       private final Attributes attributes;
-
-       /** The provided attributes is wrapped, not copied. */
-       public AttributesDictionary(Attributes attributes) {
-               if (attributes == null)
-                       throw new IllegalArgumentException("Attributes cannot be null");
-               this.attributes = attributes;
-       }
-
-       @Override
-       public int size() {
-               return attributes.size();
-       }
-
-       @Override
-       public boolean isEmpty() {
-               return attributes.size() == 0;
-       }
-
-       @Override
-       public Enumeration<String> keys() {
-               NamingEnumeration<String> namingEnumeration = attributes.getIDs();
-               return new Enumeration<String>() {
-
-                       @Override
-                       public boolean hasMoreElements() {
-                               return namingEnumeration.hasMoreElements();
-                       }
-
-                       @Override
-                       public String nextElement() {
-                               return namingEnumeration.nextElement();
-                       }
-
-               };
-       }
-
-       @Override
-       public Enumeration<Object> elements() {
-               NamingEnumeration<String> namingEnumeration = attributes.getIDs();
-               return new Enumeration<Object>() {
-
-                       @Override
-                       public boolean hasMoreElements() {
-                               return namingEnumeration.hasMoreElements();
-                       }
-
-                       @Override
-                       public Object nextElement() {
-                               String key = namingEnumeration.nextElement();
-                               return get(key);
-                       }
-
-               };
-       }
-
-       @Override
-       /** @returns a <code>String</code> or <code>String[]</code> */
-       public Object get(Object key) {
-               try {
-                       if (key == null)
-                               throw new IllegalArgumentException("Key cannot be null");
-                       Attribute attr = attributes.get(key.toString());
-                       if (attr == null)
-                               return null;
-                       if (attr.size() == 0)
-                               throw new IllegalStateException("There must be at least one value");
-                       else if (attr.size() == 1) {
-                               return attr.get().toString();
-                       } else {// multiple
-                               String[] res = new String[attr.size()];
-                               for (int i = 0; i < attr.size(); i++) {
-                                       Object value = attr.get();
-                                       if (value == null)
-                                               throw new RuntimeException("Values cannot be null");
-                                       res[i] = attr.get(i).toString();
-                               }
-                               return res;
-                       }
-               } catch (NamingException e) {
-                       throw new RuntimeException("Cannot get value for " + key, e);
-               }
-       }
-
-       @Override
-       public Object put(String key, Object value) {
-               if (key == null)
-                       throw new IllegalArgumentException("Key cannot be null");
-               if (value == null)
-                       throw new IllegalArgumentException("Value cannot be null");
-
-               Object oldValue = get(key);
-               Attribute attr = attributes.get(key);
-               if (attr == null) {
-                       attr = new BasicAttribute(key);
-                       attributes.put(attr);
-               }
-
-               if (value instanceof String[]) {
-                       String[] values = (String[]) value;
-                       // clean additional values
-                       for (int i = values.length; i < attr.size(); i++)
-                               attr.remove(i);
-                       // set values
-                       for (int i = 0; i < values.length; i++) {
-                               attr.set(i, values[i]);
-                       }
-               } else {
-                       if (attr.size() > 1)
-                               throw new IllegalArgumentException("Attribute " + key + " is multi-valued");
-                       if (attr.size() == 1) {
-                               try {
-                                       if (!attr.get(0).equals(value))
-                                               attr.set(0, value.toString());
-                               } catch (NamingException e) {
-                                       throw new RuntimeException("Cannot check existing value", e);
-                               }
-                       } else {
-                               attr.add(value.toString());
-                       }
-               }
-               return oldValue;
-       }
-
-       @Override
-       public Object remove(Object key) {
-               if (key == null)
-                       throw new IllegalArgumentException("Key cannot be null");
-               Object oldValue = get(key);
-               if (oldValue == null)
-                       return null;
-               return attributes.remove(key.toString());
-       }
-
-       /**
-        * Copy the <b>content</b> of an {@link javax.naming.Attributes} to the
-        * provided {@link Dictionary}.
-        */
-       public static void copy(Attributes attributes, Dictionary<String, Object> dictionary) {
-               AttributesDictionary ad = new AttributesDictionary(attributes);
-               Enumeration<String> keys = ad.keys();
-               while (keys.hasMoreElements()) {
-                       String key = keys.nextElement();
-                       dictionary.put(key, ad.get(key));
-               }
-       }
-
-       /**
-        * Copy a {@link Dictionary} into an {@link javax.naming.Attributes}.
-        */
-       public static void copy(Dictionary<String, Object> dictionary, Attributes attributes) {
-               AttributesDictionary ad = new AttributesDictionary(attributes);
-               Enumeration<String> keys = dictionary.keys();
-               while (keys.hasMoreElements()) {
-                       String key = keys.nextElement();
-                       ad.put(key, dictionary.get(key));
-               }
-       }
-}
diff --git a/org.argeo.security.core/src/org/argeo/util/naming/LdifParser.java b/org.argeo.security.core/src/org/argeo/util/naming/LdifParser.java
deleted file mode 100644 (file)
index ec73e8a..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-package org.argeo.util.naming;
-
-import static org.argeo.osgi.useradmin.LdifName.dn;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.List;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import javax.naming.InvalidNameException;
-import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.BasicAttribute;
-import javax.naming.directory.BasicAttributes;
-import javax.naming.ldap.LdapName;
-import javax.naming.ldap.Rdn;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.osgi.useradmin.UserDirectoryException;
-
-/** Basic LDIF parser. */
-public class LdifParser {
-       private final static Log log = LogFactory.getLog(LdifParser.class);
-
-       protected Attributes addAttributes(SortedMap<LdapName, Attributes> res, int lineNumber, LdapName currentDn,
-                       Attributes currentAttributes) {
-               try {
-                       Rdn nameRdn = currentDn.getRdn(currentDn.size() - 1);
-                       Attribute nameAttr = currentAttributes.get(nameRdn.getType());
-                       if (nameAttr == null)
-                               currentAttributes.put(nameRdn.getType(), nameRdn.getValue());
-                       else if (!nameAttr.get().equals(nameRdn.getValue()))
-                               throw new UserDirectoryException(
-                                               "Attribute " + nameAttr.getID() + "=" + nameAttr.get() + " not consistent with DN " + currentDn
-                                                               + " (shortly before line " + lineNumber + " in LDIF file)");
-                       Attributes previous = res.put(currentDn, currentAttributes);
-                       if (log.isTraceEnabled())
-                               log.trace("Added " + currentDn);
-                       return previous;
-               } catch (NamingException e) {
-                       throw new UserDirectoryException("Cannot add " + currentDn, e);
-               }
-       }
-
-       public SortedMap<LdapName, Attributes> read(InputStream in) throws IOException {
-               SortedMap<LdapName, Attributes> res = new TreeMap<LdapName, Attributes>();
-               try {
-                       List<String> lines = new ArrayList<>();
-                       try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
-                               String line;
-                               while ((line = br.readLine()) != null) {
-                                       lines.add(line);
-                               }
-                       }
-                       if (lines.size() == 0)
-                               return res;
-                       // add an empty new line since the last line is not checked
-                       if (!lines.get(lines.size() - 1).equals(""))
-                               lines.add("");
-
-                       LdapName currentDn = null;
-                       Attributes currentAttributes = null;
-                       StringBuilder currentEntry = new StringBuilder();
-
-                       readLines: for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
-                               String line = lines.get(lineNumber);
-                               boolean isLastLine = false;
-                               if (lineNumber == lines.size() - 1)
-                                       isLastLine = true;
-                               if (line.startsWith(" ")) {
-                                       currentEntry.append(line.substring(1));
-                                       if (!isLastLine)
-                                               continue readLines;
-                               }
-
-                               if (currentEntry.length() != 0 || isLastLine) {
-                                       // read previous attribute
-                                       StringBuilder attrId = new StringBuilder(8);
-                                       boolean isBase64 = false;
-                                       readAttrId: for (int i = 0; i < currentEntry.length(); i++) {
-                                               char c = currentEntry.charAt(i);
-                                               if (c == ':') {
-                                                       if (i + 1 < currentEntry.length() && currentEntry.charAt(i + 1) == ':')
-                                                               isBase64 = true;
-                                                       currentEntry.delete(0, i + (isBase64 ? 2 : 1));
-                                                       break readAttrId;
-                                               } else {
-                                                       attrId.append(c);
-                                               }
-                                       }
-
-                                       String attributeId = attrId.toString();
-                                       String cleanValueStr = currentEntry.toString().trim();
-                                       Object attributeValue = isBase64 ? Base64.getDecoder().decode(cleanValueStr) : cleanValueStr;
-
-                                       // manage DN attributes
-                                       if (attributeId.equals(dn.name()) || isLastLine) {
-                                               if (currentDn != null) {
-                                                       //
-                                                       // ADD
-                                                       //
-                                                       Attributes previous = addAttributes(res, lineNumber, currentDn, currentAttributes);
-                                                       if (previous != null) {
-                                                               log.warn("There was already an entry with DN " + currentDn
-                                                                               + ", which has been discarded by a subsequent one.");
-                                                       }
-                                               }
-
-                                               if (attributeId.equals(dn.name()))
-                                                       try {
-                                                               currentDn = new LdapName(attributeValue.toString());
-                                                               currentAttributes = new BasicAttributes(true);
-                                                       } catch (InvalidNameException e) {
-                                                               log.error(attributeValue + " not a valid DN, skipping the entry.");
-                                                               currentDn = null;
-                                                               currentAttributes = null;
-                                                       }
-                                       }
-
-                                       // store attribute
-                                       if (currentAttributes != null) {
-                                               Attribute attribute = currentAttributes.get(attributeId);
-                                               if (attribute == null) {
-                                                       attribute = new BasicAttribute(attributeId);
-                                                       currentAttributes.put(attribute);
-                                               }
-                                               attribute.add(attributeValue);
-                                       }
-                                       currentEntry = new StringBuilder();
-                               }
-                               currentEntry.append(line);
-                       }
-               } finally {
-                       in.close();
-               }
-               return res;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.core/src/org/argeo/util/naming/LdifWriter.java b/org.argeo.security.core/src/org/argeo/util/naming/LdifWriter.java
deleted file mode 100644 (file)
index 37d90b4..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.argeo.util.naming;
-
-import static org.argeo.osgi.useradmin.LdifName.dn;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.util.Base64;
-import java.util.Map;
-
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.ldap.LdapName;
-import javax.naming.ldap.Rdn;
-
-import org.argeo.osgi.useradmin.UserDirectoryException;
-
-/** Basic LDIF writer */
-public class LdifWriter {
-       private final Writer writer;
-
-       /** Writer must be closed by caller */
-       public LdifWriter(Writer writer) {
-               this.writer = writer;
-       }
-
-       /** Stream must be closed by caller */
-       public LdifWriter(OutputStream out) {
-               this(new OutputStreamWriter(out));
-       }
-
-       public void writeEntry(LdapName name, Attributes attributes) throws IOException {
-               try {
-                       // check consistency
-                       Rdn nameRdn = name.getRdn(name.size() - 1);
-                       Attribute nameAttr = attributes.get(nameRdn.getType());
-                       if (!nameAttr.get().equals(nameRdn.getValue()))
-                               throw new UserDirectoryException(
-                                               "Attribute " + nameAttr.getID() + "=" + nameAttr.get() + " not consistent with DN " + name);
-
-                       writer.append(dn.name() + ":").append(name.toString()).append('\n');
-                       Attribute objectClassAttr = attributes.get("objectClass");
-                       if (objectClassAttr != null)
-                               writeAttribute(objectClassAttr);
-                       for (NamingEnumeration<? extends Attribute> attrs = attributes.getAll(); attrs.hasMore();) {
-                               Attribute attribute = attrs.next();
-                               if (attribute.getID().equals(dn.name()) || attribute.getID().equals("objectClass"))
-                                       continue;// skip DN attribute
-                               writeAttribute(attribute);
-                       }
-                       writer.append('\n');
-                       writer.flush();
-               } catch (NamingException e) {
-                       throw new UserDirectoryException("Cannot write LDIF", e);
-               }
-       }
-
-       public void write(Map<LdapName, Attributes> entries) throws IOException {
-               for (LdapName dn : entries.keySet())
-                       writeEntry(dn, entries.get(dn));
-       }
-
-       protected void writeAttribute(Attribute attribute) throws NamingException, IOException {
-               for (NamingEnumeration<?> attrValues = attribute.getAll(); attrValues.hasMore();) {
-                       Object value = attrValues.next();
-                       if (value instanceof byte[]) {
-                               String encoded = Base64.getEncoder().encodeToString((byte[]) value);
-                               writer.append(attribute.getID()).append("::").append(encoded).append('\n');
-                       } else {
-                               writer.append(attribute.getID()).append(':').append(value.toString()).append('\n');
-                       }
-               }
-       }
-}
diff --git a/org.argeo.security.jackrabbit/.classpath b/org.argeo.security.jackrabbit/.classpath
deleted file mode 100644 (file)
index a8a298a..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="src" path="ext/test"/>
-       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
-       <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/org.argeo.security.jackrabbit/.project b/org.argeo.security.jackrabbit/.project
deleted file mode 100644 (file)
index 35bcca2..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.security.jackrabbit</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.pde.PluginNature</nature>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-       </natures>
-</projectDescription>
diff --git a/org.argeo.security.jackrabbit/bnd.bnd b/org.argeo.security.jackrabbit/bnd.bnd
deleted file mode 100644 (file)
index 6bab2aa..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-Fragment-Host: org.apache.jackrabbit.core
-Import-Package: org.springframework.core,\
-*
diff --git a/org.argeo.security.jackrabbit/build.properties b/org.argeo.security.jackrabbit/build.properties
deleted file mode 100644 (file)
index 0cc2cd0..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-source.. = src/,\
-           ext/test/
-
-additional.bundles = org.junit,\
-                     org.apache.jackrabbit.core,\
-                     javax.jcr,\
-                     org.apache.jackrabbit.api,\
-                     org.apache.jackrabbit.data,\
-                     org.apache.jackrabbit.jcr.commons,\
-                     org.apache.jackrabbit.spi,\
-                     org.apache.jackrabbit.spi.commons,\
-                     org.slf4j.api,\
-                     org.slf4j.commons.logging,\
-                     org.slf4j.log4j12,\
-                     org.apache.log4j,\
-                     org.apache.commons.collections,\
-                     EDU.oswego.cs.dl.util.concurrent,\
-                     org.apache.lucene,\
-                     org.apache.tika.core,\
-                     org.apache.commons.dbcp,\
-                     org.apache.commons.pool,\
-                     org.argeo.server.jcr
-
diff --git a/org.argeo.security.jackrabbit/ext/test/log4j.properties b/org.argeo.security.jackrabbit/ext/test/log4j.properties
deleted file mode 100644 (file)
index b4edd7c..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-log4j.rootLogger=WARN, console
-
-## Levels
-log4j.logger.org.argeo=DEBUG
-log4j.logger.org.apache.jackrabbit=OFF
-log4j.logger.org.apache.jackrabbit.core.security=DEBUG
-log4j.logger.org.apache.jackrabbit.core.DefaultSecurityManager=DEBUG
-
-## Appenders
-# console is set to be a ConsoleAppender.
-log4j.appender.console=org.apache.log4j.ConsoleAppender
-
-# console uses PatternLayout.
-log4j.appender.console.layout=org.apache.log4j.PatternLayout
-#log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n
-#log4j.appender.console.layout.ConversionPattern=%m%n
-log4j.appender.console.layout.ConversionPattern=%d{ABSOLUTE} %m (%F:%L) [%t] %p %n
diff --git a/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java b/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/JackrabbitAuthTest.java
deleted file mode 100644 (file)
index 47afff9..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.argeo.security.jackrabbit;
-
-import javax.jcr.Repository;
-import javax.jcr.Session;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase;
-
-public class JackrabbitAuthTest extends AbstractJackrabbitTestCase {
-       private final Log log = LogFactory.getLog(JackrabbitAuthTest.class);
-
-       public void testLogin() throws Exception {
-               Session session = session();
-               log.debug(session.getUserID());
-               assertEquals("admin", session.getUserID());
-               // Subject subject = new Subject();
-               // LoginContext loginContext = new LoginContext("SYSTEM", subject);
-               // loginContext.login();
-               // Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
-               //
-               // @Override
-               // public Void run() throws Exception {
-               // Repository repository = getRepository();
-               // Session session = repository.login();
-               // log.debug(session.getUserID());
-               // return null;
-               // }
-               // });
-       }
-
-       @Override
-       protected String getLoginContext() {
-               return LOGIN_CONTEXT_TEST_SYSTEM;
-       }
-
-       @Override
-       protected Repository createRepository() throws Exception {
-               return super.createRepository();
-       }
-
-       @Override
-       protected void clearRepository(Repository repository) throws Exception {
-               // System.setProperty("java.security.auth.login.config", "");
-       }
-
-       @Override
-       protected String getRepositoryConfigResource() {
-               return "/org/argeo/security/jackrabbit/repository-memory-test.xml";
-       }
-
-}
diff --git a/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/repository-memory-test.xml b/org.argeo.security.jackrabbit/ext/test/org/argeo/security/jackrabbit/repository-memory-test.xml
deleted file mode 100644 (file)
index e285555..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0"?>
-<!--
-
-    Copyright (C) 2007-2012 Argeo GmbH
-
-    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.
-
--->
-<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
-                            "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
-<Repository>
-       <!-- File system and datastore -->
-       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-
-       <!-- Workspace templates -->
-       <Workspaces rootPath="${rep.home}/workspaces"
-               defaultWorkspace="main" configRootPath="/workspaces" />
-       <Workspace name="${wsp.name}">
-               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
-                       <param name="blobFSBlockSize" value="1" />
-               </PersistenceManager>
-               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-                       <param name="path" value="${rep.home}/repository/index" />
-                       <param name="directoryManagerClass"
-                               value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
-                       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-               </SearchIndex>
-       </Workspace>
-
-       <!-- Versioning -->
-       <Versioning rootPath="${rep.home}/version">
-               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
-                       <param name="blobFSBlockSize" value="1" />
-               </PersistenceManager>
-       </Versioning>
-
-       <!-- Indexing -->
-       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-               <param name="path" value="${rep.home}/repository/index" />
-               <param name="directoryManagerClass"
-                       value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
-               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-       </SearchIndex>
-
-       <!-- Security -->
-       <Security appName="Jackrabbit">
-               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
-                       workspaceName="security"/>
-               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager"/>
-       </Security>
-</Repository>
\ No newline at end of file
diff --git a/org.argeo.security.jackrabbit/pom.xml b/org.argeo.security.jackrabbit/pom.xml
deleted file mode 100644 (file)
index 1dfc103..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-       <modelVersion>4.0.0</modelVersion>
-       <parent>
-               <groupId>org.argeo.commons</groupId>
-               <artifactId>argeo-commons</artifactId>
-               <version>2.1.46-SNAPSHOT</version>
-               <relativePath>..</relativePath>
-       </parent>
-       <artifactId>org.argeo.security.jackrabbit</artifactId>
-       <name>Commons Jackrabbit Extensions</name>
-       <dependencies>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.cms.api</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-
-               <!-- TESTING -->
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.server.jcr</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-                       <scope>test</scope>
-               </dependency>
-       </dependencies>
-</project>
\ No newline at end of file
diff --git a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessControlProvider.java b/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessControlProvider.java
deleted file mode 100644 (file)
index cd0cf86..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.argeo.security.jackrabbit;
-
-import java.util.Map;
-
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.jackrabbit.core.security.authorization.acl.ACLProvider;
-
-/** Argeo specific access control provider */
-public class ArgeoAccessControlProvider extends ACLProvider {
-
-       @SuppressWarnings({ "rawtypes", "unchecked" })
-       @Override
-       public void init(Session systemSession, Map configuration)
-                       throws RepositoryException {
-               if (!configuration.containsKey(PARAM_ALLOW_UNKNOWN_PRINCIPALS))
-                       configuration.put(PARAM_ALLOW_UNKNOWN_PRINCIPALS, "true");
-               super.init(systemSession, configuration);
-       }
-
-}
diff --git a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessManager.java b/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoAccessManager.java
deleted file mode 100644 (file)
index 52ea3c9..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.jackrabbit;
-
-import javax.jcr.PathNotFoundException;
-import javax.jcr.RepositoryException;
-import javax.jcr.security.Privilege;
-
-import org.apache.jackrabbit.core.id.ItemId;
-import org.apache.jackrabbit.core.security.DefaultAccessManager;
-import org.apache.jackrabbit.spi.Path;
-
-/**
- * Intermediary class in order to have a consistent naming in config files. Does
- * nothing for the time being, but may in the future.
- */
-public class ArgeoAccessManager extends DefaultAccessManager {
-
-       @Override
-       public boolean canRead(Path itemPath, ItemId itemId)
-                       throws RepositoryException {
-               return super.canRead(itemPath, itemId);
-       }
-
-       @Override
-       public Privilege[] getPrivileges(String absPath)
-                       throws PathNotFoundException, RepositoryException {
-               return super.getPrivileges(absPath);
-       }
-
-       @Override
-       public boolean hasPrivileges(String absPath, Privilege[] privileges)
-                       throws PathNotFoundException, RepositoryException {
-               return super.hasPrivileges(absPath, privileges);
-       }
-
-}
diff --git a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java b/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java
deleted file mode 100644 (file)
index 978be43..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.jackrabbit;
-
-import java.security.Principal;
-import java.util.Set;
-
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.security.auth.Subject;
-import javax.security.auth.x500.X500Principal;
-
-import org.apache.jackrabbit.api.security.user.UserManager;
-import org.apache.jackrabbit.core.DefaultSecurityManager;
-import org.apache.jackrabbit.core.security.AMContext;
-import org.apache.jackrabbit.core.security.AccessManager;
-import org.apache.jackrabbit.core.security.SecurityConstants;
-import org.apache.jackrabbit.core.security.authorization.WorkspaceAccessManager;
-
-/** Integrates Spring Security and Jackrabbit Security users and roles. */
-public class ArgeoSecurityManager extends DefaultSecurityManager {
-       @Override
-       public AccessManager getAccessManager(Session session, AMContext amContext)
-                       throws RepositoryException {
-               synchronized (getSystemSession()) {
-                       return super.getAccessManager(session, amContext);
-               }
-       }
-
-       @Override
-       public UserManager getUserManager(Session session)
-                       throws RepositoryException {
-               synchronized (getSystemSession()) {
-                       return super.getUserManager(session);
-               }
-       }
-
-       /**
-        * Since this is called once when the session is created, we take the
-        * opportunity to make sure that Jackrabbit users and groups reflect Spring
-        * Security name and authorities.
-        */
-       @Override
-       public String getUserID(Subject subject, String workspaceName)
-                       throws RepositoryException {
-               Set<X500Principal> userPrincipal = subject
-                               .getPrincipals(X500Principal.class);
-               if (userPrincipal.isEmpty())
-                       return super.getUserID(subject, workspaceName);
-               if (userPrincipal.size() > 1) {
-                       StringBuilder buf = new StringBuilder();
-                       for (X500Principal principal : userPrincipal)
-                               buf.append(' ').append('\"').append(principal).append('\"');
-                       throw new RuntimeException("Multiple user principals:" + buf);
-               }
-               return userPrincipal.iterator().next().getName();
-               // Authentication authentication = SecurityContextHolder.getContext()
-               // .getAuthentication();
-               // if (authentication != null)
-               // return authentication.getName();
-               // else
-               // return super.getUserID(subject, workspaceName);
-       }
-
-       @Override
-       protected WorkspaceAccessManager createDefaultWorkspaceAccessManager() {
-               WorkspaceAccessManager wam = super
-                               .createDefaultWorkspaceAccessManager();
-               return new ArgeoWorkspaceAccessManagerImpl(wam);
-       }
-
-       private class ArgeoWorkspaceAccessManagerImpl implements SecurityConstants,
-                       WorkspaceAccessManager {
-               private final WorkspaceAccessManager wam;
-
-               public ArgeoWorkspaceAccessManagerImpl(WorkspaceAccessManager wam) {
-                       super();
-                       this.wam = wam;
-               }
-
-               public void init(Session systemSession) throws RepositoryException {
-                       wam.init(systemSession);
-               }
-
-               public void close() throws RepositoryException {
-               }
-
-               public boolean grants(Set<Principal> principals, String workspaceName)
-                               throws RepositoryException {
-                       // TODO: implements finer access to workspaces
-                       return true;
-               }
-       }
-
-}
diff --git a/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java b/org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java
deleted file mode 100644 (file)
index 62f8fa0..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.argeo.security.jackrabbit;
-
-import java.util.Map;
-import java.util.Set;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.login.LoginException;
-import javax.security.auth.spi.LoginModule;
-import javax.security.auth.x500.X500Principal;
-
-import org.apache.jackrabbit.core.security.SecurityConstants;
-import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
-import org.argeo.node.DataAdminPrincipal;
-
-public class SystemJackrabbitLoginModule implements LoginModule {
-
-       private Subject subject;
-
-       @Override
-       public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
-                       Map<String, ?> options) {
-               this.subject = subject;
-       }
-
-       @Override
-       public boolean login() throws LoginException {
-               return true;
-       }
-
-       @Override
-       public boolean commit() throws LoginException {
-               Set<DataAdminPrincipal> initPrincipal = subject.getPrincipals(DataAdminPrincipal.class);
-               if (!initPrincipal.isEmpty()) {
-                       subject.getPrincipals().add(new AdminPrincipal(SecurityConstants.ADMIN_ID));
-                       return true;
-               }
-
-               Set<X500Principal> userPrincipal = subject.getPrincipals(X500Principal.class);
-               if (userPrincipal.isEmpty())
-                       throw new LoginException("Subject must be pre-authenticated");
-               if (userPrincipal.size() > 1)
-                       throw new LoginException("Multiple user principals " + userPrincipal);
-
-               return true;
-       }
-
-       @Override
-       public boolean abort() throws LoginException {
-               return true;
-       }
-
-       @Override
-       public boolean logout() throws LoginException {
-               Set<DataAdminPrincipal> initPrincipal = subject.getPrincipals(DataAdminPrincipal.class);
-               if (!initPrincipal.isEmpty()) {
-                       subject.getPrincipals(AdminPrincipal.class);
-                       return true;
-               }
-               return true;
-       }
-}
diff --git a/org.argeo.security.ui.admin/.classpath b/org.argeo.security.ui.admin/.classpath
deleted file mode 100644 (file)
index 457b115..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="src" path="src" />
-       <classpathentry kind="con"
-               path="org.eclipse.pde.core.requiredPlugins" />
-       <classpathentry kind="con"
-               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" />
-       <classpathentry kind="output" path="bin" />
-</classpath>
diff --git a/org.argeo.security.ui.admin/.project b/org.argeo.security.ui.admin/.project
deleted file mode 100644 (file)
index 9821dd9..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.security.ui.admin</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.pde.PluginNature</nature>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-       </natures>
-</projectDescription>
diff --git a/org.argeo.security.ui.admin/META-INF/spring/commands.xml b/org.argeo.security.ui.admin/META-INF/spring/commands.xml
deleted file mode 100644 (file)
index 7d39876..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans
-        http://www.springframework.org/schema/beans/spring-beans.xsd">
-
-       <!-- USERS CRUDS -->
-       <bean id="newUser" class="org.argeo.security.ui.admin.internal.commands.NewUser"
-               scope="prototype">
-               <property name="userAdminWrapper" ref="userAdminWrapper" />
-       </bean>
-       <bean id="deleteUsers"
-               class="org.argeo.security.ui.admin.internal.commands.DeleteUsers"
-               scope="prototype">
-               <property name="userAdminWrapper" ref="userAdminWrapper" />
-       </bean>
-       <bean id="userBatchUpdate"
-               class="org.argeo.security.ui.admin.internal.commands.UserBatchUpdate"
-               scope="prototype">
-               <property name="userAdminWrapper" ref="userAdminWrapper" />
-       </bean>
-       <!-- GROUPS CRUDS -->
-       <bean id="newGroup" class="org.argeo.security.ui.admin.internal.commands.NewGroup"
-               scope="prototype">
-               <property name="userAdminWrapper" ref="userAdminWrapper" />
-       </bean>
-       <bean id="deleteGroups"
-               class="org.argeo.security.ui.admin.internal.commands.DeleteGroups"
-               scope="prototype">
-               <property name="userAdminWrapper" ref="userAdminWrapper" />
-       </bean>
-
-       <!-- TRANSACTIONS -->
-       <bean id="userTransactionHandler"
-               class="org.argeo.security.ui.admin.internal.commands.UserTransactionHandler"
-               scope="prototype">
-               <property name="userAdminWrapper" ref="userAdminWrapper" />
-       </bean>
-</beans>
diff --git a/org.argeo.security.ui.admin/META-INF/spring/common.xml b/org.argeo.security.ui.admin/META-INF/spring/common.xml
deleted file mode 100644 (file)
index f3b7ecb..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>\r
-<beans xmlns="http://www.springframework.org/schema/beans"\r
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
-       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
-\r
-       <bean id="userTransactionProvider"\r
-               class="org.argeo.security.ui.admin.internal.providers.UserTransactionProvider"\r
-               scope="singleton" lazy-init="false">\r
-               <property name="userTransaction" ref="userTransaction" />\r
-       </bean>\r
-\r
-       <bean id="userAdminWrapper" class="org.argeo.security.ui.admin.internal.UserAdminWrapper"\r
-               scope="singleton" lazy-init="false">\r
-               <property name="userTransaction" ref="userTransaction" />\r
-               <property name="userAdmin" ref="userAdmin" />\r
-               <property name="userAdminServiceReference" ref="userAdmin" />\r
-       </bean>\r
-\r
-       <bean\r
-               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">\r
-               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />\r
-               <property name="locations">\r
-                       <value>osgibundle:security-admin.properties</value>\r
-               </property>\r
-       </bean>\r
-\r
-</beans>
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/META-INF/spring/osgi.xml b/org.argeo.security.ui.admin/META-INF/spring/osgi.xml
deleted file mode 100644 (file)
index 02a6374..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>\r
-<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
-       xmlns:osgi="http://www.springframework.org/schema/osgi"\r
-       xsi:schemaLocation="http://www.springframework.org/schema/osgi  \r
-       http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
-       http://www.springframework.org/schema/beans   \r
-       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"\r
-       osgi:default-timeout="30000">\r
-\r
-       <reference id="nodeRepository" interface="javax.jcr.Repository"\r
-               filter="(argeo.jcr.repository.alias=node)" />\r
-               \r
-       <!-- New user admin -->\r
-       <reference id="userAdmin" interface="org.osgi.service.useradmin.UserAdmin" />\r
-       <reference id="userTransaction" interface="javax.transaction.UserTransaction" />\r
-</beans:beans>
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/META-INF/spring/parts.xml b/org.argeo.security.ui.admin/META-INF/spring/parts.xml
deleted file mode 100644 (file)
index cbc36e0..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans
-        http://www.springframework.org/schema/beans/spring-beans.xsd">
-
-
-       <!-- Editors -->
-       <bean id="userEditor" class="org.argeo.security.ui.admin.internal.parts.UserEditor"
-               scope="prototype">
-               <property name="userAdminWrapper" ref="userAdminWrapper" />
-       </bean>
-
-       <bean id="groupEditor" class="org.argeo.security.ui.admin.internal.parts.UserEditor"
-               scope="prototype">
-               <property name="userAdminWrapper" ref="userAdminWrapper" />
-       </bean>
-
-       <!-- Views -->
-       <bean id="usersView" class="org.argeo.security.ui.admin.internal.parts.UsersView"
-               scope="prototype">
-               <property name="userAdminWrapper" ref="userAdminWrapper" />
-       </bean>
-       <bean id="groupsView" class="org.argeo.security.ui.admin.internal.parts.GroupsView"
-               scope="prototype">
-               <property name="userAdminWrapper" ref="userAdminWrapper" />
-       </bean>
-
-</beans>
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/bnd.bnd b/org.argeo.security.ui.admin/bnd.bnd
deleted file mode 100644 (file)
index d5f00ce..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-Bundle-SymbolicName: org.argeo.security.ui.admin;singleton:=true
-Bundle-Activator: org.argeo.security.ui.admin.SecurityAdminPlugin
-Bundle-ActivationPolicy: lazy
-
-Require-Bundle:        org.eclipse.core.runtime
-
-Import-Package:        org.eclipse.core.runtime.jobs,\
-org.argeo.cms.auth,\
-org.argeo.eclipse.spring,\
-org.eclipse.jface.window,\
-org.eclipse.swt,\
-org.eclipse.swt.widgets,\
-org.eclipse.ui.services,\
-org.osgi.framework,\
-org.springframework.core,\
-*                              
diff --git a/org.argeo.security.ui.admin/build.properties b/org.argeo.security.ui.admin/build.properties
deleted file mode 100644 (file)
index 30f7153..0000000
+++ /dev/null
@@ -1 +0,0 @@
-source.. = src/
diff --git a/org.argeo.security.ui.admin/icons/add.gif b/org.argeo.security.ui.admin/icons/add.gif
deleted file mode 100644 (file)
index 252d7eb..0000000
Binary files a/org.argeo.security.ui.admin/icons/add.gif and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/batch.gif b/org.argeo.security.ui.admin/icons/batch.gif
deleted file mode 100644 (file)
index b8ca14a..0000000
Binary files a/org.argeo.security.ui.admin/icons/batch.gif and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/begin.gif b/org.argeo.security.ui.admin/icons/begin.gif
deleted file mode 100755 (executable)
index feb8e94..0000000
Binary files a/org.argeo.security.ui.admin/icons/begin.gif and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/clear.gif b/org.argeo.security.ui.admin/icons/clear.gif
deleted file mode 100644 (file)
index 6bc10f9..0000000
Binary files a/org.argeo.security.ui.admin/icons/clear.gif and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/commit.gif b/org.argeo.security.ui.admin/icons/commit.gif
deleted file mode 100755 (executable)
index 876f3eb..0000000
Binary files a/org.argeo.security.ui.admin/icons/commit.gif and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/refresh.png b/org.argeo.security.ui.admin/icons/refresh.png
deleted file mode 100644 (file)
index a3884fb..0000000
Binary files a/org.argeo.security.ui.admin/icons/refresh.png and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/remove.gif b/org.argeo.security.ui.admin/icons/remove.gif
deleted file mode 100644 (file)
index 0ae6dec..0000000
Binary files a/org.argeo.security.ui.admin/icons/remove.gif and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/role.gif b/org.argeo.security.ui.admin/icons/role.gif
deleted file mode 100644 (file)
index 274a850..0000000
Binary files a/org.argeo.security.ui.admin/icons/role.gif and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/rollback.gif b/org.argeo.security.ui.admin/icons/rollback.gif
deleted file mode 100755 (executable)
index c753995..0000000
Binary files a/org.argeo.security.ui.admin/icons/rollback.gif and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/save.gif b/org.argeo.security.ui.admin/icons/save.gif
deleted file mode 100644 (file)
index 654ad7b..0000000
Binary files a/org.argeo.security.ui.admin/icons/save.gif and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/save_security.png b/org.argeo.security.ui.admin/icons/save_security.png
deleted file mode 100644 (file)
index ca41dc9..0000000
Binary files a/org.argeo.security.ui.admin/icons/save_security.png and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/save_security_disabled.png b/org.argeo.security.ui.admin/icons/save_security_disabled.png
deleted file mode 100644 (file)
index fb7d08d..0000000
Binary files a/org.argeo.security.ui.admin/icons/save_security_disabled.png and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/security.gif b/org.argeo.security.ui.admin/icons/security.gif
deleted file mode 100644 (file)
index 57fb95e..0000000
Binary files a/org.argeo.security.ui.admin/icons/security.gif and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/sync.gif b/org.argeo.security.ui.admin/icons/sync.gif
deleted file mode 100644 (file)
index b4fa052..0000000
Binary files a/org.argeo.security.ui.admin/icons/sync.gif and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/user.gif b/org.argeo.security.ui.admin/icons/user.gif
deleted file mode 100644 (file)
index 90a0014..0000000
Binary files a/org.argeo.security.ui.admin/icons/user.gif and /dev/null differ
diff --git a/org.argeo.security.ui.admin/icons/users.gif b/org.argeo.security.ui.admin/icons/users.gif
deleted file mode 100644 (file)
index 2de7edd..0000000
Binary files a/org.argeo.security.ui.admin/icons/users.gif and /dev/null differ
diff --git a/org.argeo.security.ui.admin/plugin.xml b/org.argeo.security.ui.admin/plugin.xml
deleted file mode 100644 (file)
index 2cf0ba2..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?eclipse version="3.4"?>
-<plugin>
-   <extension
-         point="org.eclipse.ui.perspectives">
-      <perspective
-            class="org.argeo.security.ui.admin.SecurityAdminPerspective"
-            icon="icons/security.gif"
-            id="org.argeo.security.ui.admin.adminSecurityPerspective"
-            name="Security">
-      </perspective>
-   </extension>
-   
-   <!-- VIEWS -->
-   <extension
-               point="org.eclipse.ui.views">
-      <view
-            class="org.argeo.eclipse.spring.SpringExtensionFactory"
-            icon="icons/users.gif"
-            id="org.argeo.security.ui.admin.usersView"
-            name="Users"
-            restorable="true">
-      </view>
-      <view
-            class="org.argeo.eclipse.spring.SpringExtensionFactory"
-            icon="icons/role.gif"
-            id="org.argeo.security.ui.admin.groupsView"
-            name="Groups"
-            restorable="false">
-      </view>
-    </extension> 
-       
-       <!-- EDITORS -->
-       <extension
-               point="org.eclipse.ui.editors">
-               <editor
-                       class="org.argeo.eclipse.spring.SpringExtensionFactory"
-            id="org.argeo.security.ui.admin.userEditor"
-            name="User"
-            icon="icons/user.gif"
-            default="false">
-               </editor>
-               <editor
-                       class="org.argeo.eclipse.spring.SpringExtensionFactory"
-            id="org.argeo.security.ui.admin.groupEditor"
-            name="User"
-            icon="icons/users.gif"
-            default="false">
-               </editor>
-       </extension>
-    
-    <extension
-         point="org.eclipse.ui.commands">
-               <!-- User CRUD -->
-               <command
-            id="org.argeo.security.ui.admin.newUser"
-            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
-            name="New User">
-       </command>
-               <command
-                       id="org.argeo.security.ui.admin.deleteUsers"
-            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
-                       name="Delete User">
-               </command>
-               <command
-                       defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
-            id="org.argeo.security.ui.admin.userBatchUpdate"
-            name="User batch update">
-               </command>
-               <!-- Group CRUD -->
-               <command
-                       id="org.argeo.security.ui.admin.newGroup"
-                       defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
-            name="New Group">
-               </command>
-               <command
-            id="org.argeo.security.ui.admin.deleteGroups"
-                       defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
-            name="Delete Group">
-               </command>
-               <!-- Transaction -->
-               <command
-                   id="org.argeo.security.ui.admin.userTransactionHandler"
-            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
-               name="Manage a user transaction">
-                       <commandParameter
-                                       id="param.commandId"
-                                       name="begin, commit or rollback">
-                       </commandParameter>
-               </command>
-
-         <!-- Force the refresh when the various listener are not enough -->
-      <command
-            defaultHandler="org.argeo.security.ui.admin.internal.commands.ForceRefresh"
-            id="org.argeo.security.ui.admin.forceRefresh"
-            name="Force Refresh">
-      </command>
-       </extension>
-       
-       <!-- MENU CONTRIBUTIONS -->
-       <extension
-               point="org.eclipse.ui.menus">
-               <menuContribution
-                       locationURI="toolbar:org.argeo.security.ui.rap.userToolbar?after=org.eclipse.ui.file.saveAll"> 
-                       <!-- Transaction management --> 
-                       <command
-                               commandId="org.argeo.security.ui.admin.userTransactionHandler"
-                               icon="icons/commit.gif"
-                               label="Commit Transaction"
-                               style="push"
-                               tooltip="Commit a user transaction">
-                               <parameter name="param.commandId" value="transaction.commit" />
-                               <visibleWhen>
-                                       <with variable="org.argeo.security.ui.admin.userTransactionState">
-                                               <equals value="status.active" />
-                                       </with>
-                               </visibleWhen>
-                       </command>
-                       <command
-                               commandId="org.argeo.security.ui.admin.userTransactionHandler"
-                               icon="icons/rollback.gif"
-                               label="Rollback Transaction"
-                               style="push"
-                               tooltip="Abandon current changes and rollback to the latest commited version">
-                               <parameter name="param.commandId" value="transaction.rollback" />
-                               <visibleWhen>
-                                       <with variable="org.argeo.security.ui.admin.userTransactionState">
-                                                       <equals value="status.active" />
-                                       </with>
-                               </visibleWhen>
-                       </command>
-               </menuContribution>
-    
-       <!-- UsersView specific toolbar menu -->
-               <menuContribution
-            locationURI="toolbar:org.argeo.security.ui.admin.usersView">
-            <command
-                  commandId="org.argeo.security.ui.admin.deleteUsers"
-                  icon="icons/remove.gif"
-                  label="Delete User"
-                  tooltip="Delete selected users">
-            </command>
-            <command
-                  commandId="org.argeo.security.ui.admin.forceRefresh"
-                  icon="icons/refresh.png"
-                  label="Refresh list"
-                  tooltip="Force the full refresh of the user list">
-            </command>
-            <command
-                  commandId="org.argeo.security.ui.admin.newUser"
-                  icon="icons/add.gif"
-                  label="Add User"
-                  tooltip="Create a new user">
-            </command>
-            <command
-                  commandId="org.argeo.security.ui.admin.userBatchUpdate"
-                  icon="icons/batch.gif"
-                  label="Update users"
-                  tooltip="Perform maintenance activities on a list of chosen users">
-            </command>
-        </menuContribution>
-
-       <!-- GroupsView specific toolbar menu -->
-        <menuContribution
-            locationURI="toolbar:org.argeo.security.ui.admin.groupsView">
-            <command
-                  commandId="org.argeo.security.ui.admin.deleteGroups"
-                  icon="icons/remove.gif"
-                  label="Delete Group"
-                  tooltip="Delete selected groups">
-            </command>
-            <command
-                  commandId="org.argeo.security.ui.admin.forceRefresh"
-                  icon="icons/refresh.png"
-                  label="Refresh list"
-                  tooltip="Force the full refresh of the group list">
-            </command>
-            <command
-                  commandId="org.argeo.security.ui.admin.newGroup"
-                  icon="icons/add.gif"
-                  label="Add Group"
-                  tooltip="Create a new group">
-            </command>
-        </menuContribution>
-         
-               <!--            <menuContribution
-            locationURI="toolbar:org.argeo.security.ui.admin.adminRolesView">
-            <command
-                  commandId="org.argeo.security.ui.admin.refreshRoles"
-                  icon="icons/sync.gif"
-                  label="LDAP Roles Sync"
-                  tooltip="Synchronize roles from LDAP">
-            </command>
-        </menuContribution> -->
-       </extension>
-
-       <!-- SERVICES -->
-       <extension
-       point="org.eclipse.ui.services">
-        <sourceProvider
-               id="org.argeo.security.ui.admin.userTransactionProvider"
-            provider="org.argeo.eclipse.spring.SpringExtensionFactory" >
-                       <variable
-                   name="org.argeo.security.ui.admin.userTransactionState"
-                   priorityLevel="workbench">
-               </variable>
-               </sourceProvider>
-       </extension>
-  
-       <!-- ACTIVITIES -->
-       <extension
-               point="org.eclipse.ui.activities">
-               <!-- group admin is intended to make all user and group maintenance operations -->
-               <!--<activityPatternBinding
-                       activityId="org.argeo.security.ui.userAdminActivity"
-                       isEqualityPattern="true"
-                       pattern="org.argeo.security.ui.admin/org.argeo.security.ui.admin.adminSecurityPerspective">
-               </activityPatternBinding>-->
-               <activityPatternBinding
-                       activityId="org.argeo.security.ui.groupAdminActivity"
-                       isEqualityPattern="true"
-                       pattern="org.argeo.security.ui.admin/org.argeo.security.ui.admin.adminSecurityPerspective">
-               </activityPatternBinding>
-       </extension>
-       
-       <!-- STARTUP  --> 
-       <extension point="org.eclipse.ui.startup">
-               <startup class="org.argeo.security.ui.admin.internal.PartStateChanged"/>
-       </extension>
-</plugin>
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/pom.xml b/org.argeo.security.ui.admin/pom.xml
deleted file mode 100644 (file)
index 3650ced..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-       <modelVersion>4.0.0</modelVersion>
-       <parent>
-               <groupId>org.argeo.commons</groupId>
-               <version>2.1.46-SNAPSHOT</version>
-               <artifactId>argeo-commons</artifactId>
-               <relativePath>..</relativePath>
-       </parent>
-       <artifactId>org.argeo.security.ui.admin</artifactId>
-       <name>Commons CMS Workbench Admin</name>
-       <packaging>jar</packaging>
-       <dependencies>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.cms</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.util</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.security.ui</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.security.core</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.eclipse.ui</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.eclipse.ui.rap</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-                       <scope>provided</scope>
-               </dependency>
-       </dependencies>
-</project>
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/security-admin.properties b/org.argeo.security.ui.admin/security-admin.properties
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminImages.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminImages.java
deleted file mode 100644 (file)
index f15f8ec..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Argeo Connect - Data management and communications
- * Copyright (C) 2012 Argeo GmbH
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>
- *
- * Additional permission under GNU GPL version 3 section 7
- *
- * If you modify this Program, or any covered work, by linking or combining it
- * with software covered by the terms of the Eclipse Public License, the
- * licensors of this Program grant you additional permission to convey the
- * resulting work. Corresponding Source for a non-source form of such a
- * combination shall include the source code for the parts of such software
- * which are used as well as that of the covered work.
- */
-package org.argeo.security.ui.admin;
-
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.swt.graphics.Image;
-
-/** Shared icons that must be declared programmatically . */
-public class SecurityAdminImages {
-       private final static String PREFIX = "icons/";
-
-       public final static ImageDescriptor ICON_REMOVE_DESC = SecurityAdminPlugin
-                       .getImageDescriptor(PREFIX + "remove.gif");
-       public final static ImageDescriptor ICON_USER_DESC = SecurityAdminPlugin
-                       .getImageDescriptor(PREFIX + "user.gif");
-       
-       public final static Image ICON_USER = ICON_USER_DESC.createImage();
-       public final static Image ICON_GROUP = SecurityAdminPlugin
-                       .getImageDescriptor(PREFIX + "users.gif").createImage();
-       public final static Image ICON_ROLE = SecurityAdminPlugin
-                       .getImageDescriptor(PREFIX + "role.gif").createImage();
-
-}
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminPerspective.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminPerspective.java
deleted file mode 100644 (file)
index 2334827..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin;
-
-import org.argeo.security.ui.admin.internal.parts.GroupsView;
-import org.argeo.security.ui.admin.internal.parts.UsersView;
-import org.eclipse.ui.IFolderLayout;
-import org.eclipse.ui.IPageLayout;
-import org.eclipse.ui.IPerspectiveFactory;
-
-/** Default perspective to manage users and groups */
-public class SecurityAdminPerspective implements IPerspectiveFactory {
-       public void createInitialLayout(IPageLayout layout) {
-               String editorArea = layout.getEditorArea();
-               layout.setEditorAreaVisible(true);
-               layout.setFixed(false);
-
-               IFolderLayout left = layout.createFolder("left", IPageLayout.LEFT,
-                               0.25f, editorArea);
-               left.addView(UsersView.ID);
-               left.addView(GroupsView.ID);
-       }
-}
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminPlugin.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/SecurityAdminPlugin.java
deleted file mode 100644 (file)
index f9c0ad9..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin;
-
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.ui.plugin.AbstractUIPlugin;
-import org.osgi.framework.BundleContext;
-
-public class SecurityAdminPlugin extends AbstractUIPlugin {
-       public static final String PLUGIN_ID = "org.argeo.security.ui.admin"; //$NON-NLS-1$
-       private static SecurityAdminPlugin plugin;
-       private static BundleContext bundleContext;
-
-       public SecurityAdminPlugin() {
-       }
-
-       public void start(BundleContext context) throws Exception {
-               super.start(context);
-               plugin = this;
-               bundleContext = context;
-       }
-
-       public void stop(BundleContext context) throws Exception {
-               plugin = null;
-               bundleContext = null;
-               super.stop(context);
-       }
-
-       public static SecurityAdminPlugin getDefault() {
-               return plugin;
-       }
-
-       public static BundleContext getBundleContext() {
-               return bundleContext;
-       }
-
-       public static ImageDescriptor getImageDescriptor(String path) {
-               return imageDescriptorFromPlugin(PLUGIN_ID, path);
-       }
-
-}
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/PartStateChanged.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/PartStateChanged.java
deleted file mode 100644 (file)
index aacf6ef..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-package org.argeo.security.ui.admin.internal;
-
-import org.argeo.cms.CmsException;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.IPartListener;
-import org.eclipse.ui.IStartup;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.PlatformUI;
-
-/** Manage transaction and part refresh while updating the security model */
-public class PartStateChanged implements IPartListener, IStartup {
-       // private final static Log log = LogFactory.getLog(PartStateChanged.class);
-       // private IContextActivation contextActivation;
-
-       @Override
-       public void earlyStartup() {
-               Display.getDefault().asyncExec(new Runnable() {
-                       public void run() {
-                               try {
-                                       IWorkbenchPage iwp = PlatformUI.getWorkbench()
-                                                       .getActiveWorkbenchWindow().getActivePage();
-                                       if (iwp != null)
-                                               iwp.addPartListener(new PartStateChanged());
-                               } catch (Exception e) {
-                                       throw new CmsException(
-                                                       "Error while registering the PartStateChangedListener",
-                                                       e);
-                               }
-                       }
-               });
-       }
-
-       @Override
-       public void partActivated(IWorkbenchPart part) {
-               // Nothing to do
-       }
-
-       @Override
-       public void partBroughtToTop(IWorkbenchPart part) {
-               // Nothing to do
-       }
-
-       @Override
-       public void partClosed(IWorkbenchPart part) {
-               // Nothing to do
-       }
-
-       @Override
-       public void partDeactivated(IWorkbenchPart part) {
-               // Nothing to do
-       }
-
-       @Override
-       public void partOpened(IWorkbenchPart part) {
-               // Nothing to do
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UiAdminUtils.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UiAdminUtils.java
deleted file mode 100644 (file)
index 871877a..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.argeo.security.ui.admin.internal;
-
-import javax.transaction.UserTransaction;
-
-import org.argeo.cms.CmsException;
-import org.argeo.security.ui.admin.internal.providers.UserTransactionProvider;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.PlatformUI;
-import org.eclipse.ui.services.ISourceProviderService;
-
-/** First effort to centralize back end methods used by the user admin UI */
-public class UiAdminUtils {
-       /*
-        * INTERNAL METHODS: Below methods are meant to stay here and are not part
-        * of a potential generic backend to manage the useradmin
-        */
-       /** Easily notify the ActiveWindow that the transaction had a state change */
-       public final static void notifyTransactionStateChange(
-                       UserTransaction userTransaction) {
-               try {
-                       IWorkbenchWindow aww = PlatformUI.getWorkbench()
-                                       .getActiveWorkbenchWindow();
-                       ISourceProviderService sourceProviderService = (ISourceProviderService) aww
-                                       .getService(ISourceProviderService.class);
-                       UserTransactionProvider esp = (UserTransactionProvider) sourceProviderService
-                                       .getSourceProvider(UserTransactionProvider.TRANSACTION_STATE);
-                       esp.fireTransactionStateChange();
-               } catch (Exception e) {
-                       throw new CmsException("Unable to begin transaction", e);
-               }
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UiUserAdminListener.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UiUserAdminListener.java
deleted file mode 100644 (file)
index 98c9836..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.argeo.security.ui.admin.internal;
-
-import org.eclipse.swt.widgets.Display;
-import org.osgi.service.useradmin.UserAdminEvent;
-import org.osgi.service.useradmin.UserAdminListener;
-
-/** Convenience class to insure the call to refresh is done in the UI thread */
-public abstract class UiUserAdminListener implements UserAdminListener {
-
-       private final Display display;
-
-       public UiUserAdminListener(Display display) {
-               this.display = display;
-       }
-
-       @Override
-       public void roleChanged(final UserAdminEvent event) {
-               display.asyncExec(new Runnable() {
-                       @Override
-                       public void run() {
-                               roleChangedToUiThread(event);
-                       }
-               });
-       }
-
-       public abstract void roleChangedToUiThread(UserAdminEvent event);
-}
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UserAdminWrapper.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/UserAdminWrapper.java
deleted file mode 100644 (file)
index d443a3d..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.argeo.security.ui.admin.internal;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.transaction.Status;
-import javax.transaction.UserTransaction;
-
-import org.argeo.cms.CmsException;
-import org.osgi.service.useradmin.UserAdminEvent;
-import org.osgi.service.useradmin.UserAdminListener;
-
-/** Centralise interaction with the UserAdmin in this bundle */
-public class UserAdminWrapper extends
-               org.argeo.cms.util.useradmin.UserAdminWrapper {
-
-       // First effort to simplify UX while managing users and groups
-       public final static boolean COMMIT_ON_SAVE = true;
-
-       // Registered listeners
-       List<UserAdminListener> listeners = new ArrayList<UserAdminListener>();
-
-       /**
-        * Starts a transaction if necessary. Should always been called together
-        * with {@link UserAdminWrapper#commitOrNotifyTransactionStateChange()} once
-        * the security model changes have been performed.
-        */
-       public UserTransaction beginTransactionIfNeeded() {
-               try {
-                       UserTransaction userTransaction = getUserTransaction();
-                       if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) {
-                               userTransaction.begin();
-                               // UiAdminUtils.notifyTransactionStateChange(userTransaction);
-                       }
-                       return userTransaction;
-               } catch (Exception e) {
-                       throw new CmsException("Unable to begin transaction", e);
-               }
-       }
-
-       /**
-        * Depending on the current application configuration, it will either commit
-        * the current transaction or throw a notification that the transaction
-        * state has changed (In the later case, it must be called from the UI
-        * thread).
-        */
-       public void commitOrNotifyTransactionStateChange() {
-               try {
-                       UserTransaction userTransaction = getUserTransaction();
-                       if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION)
-                               return;
-
-                       if (UserAdminWrapper.COMMIT_ON_SAVE)
-                               userTransaction.commit();
-                       else
-                               UiAdminUtils.notifyTransactionStateChange(userTransaction);
-               } catch (Exception e) {
-                       throw new CmsException("Unable to clean transaction", e);
-               }
-       }
-
-       // TODO implement safer mechanism
-       public void addListener(UserAdminListener userAdminListener) {
-               if (!listeners.contains(userAdminListener))
-                       listeners.add(userAdminListener);
-       }
-
-       public void removeListener(UserAdminListener userAdminListener) {
-               if (listeners.contains(userAdminListener))
-                       listeners.remove(userAdminListener);
-       }
-
-       public void notifyListeners(UserAdminEvent event) {
-               for (UserAdminListener listener : listeners)
-                       listener.roleChanged(event);
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/DeleteGroups.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/DeleteGroups.java
deleted file mode 100644 (file)
index df5d430..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.commands;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.argeo.security.ui.admin.SecurityAdminPlugin;
-import org.argeo.security.ui.admin.internal.UserAdminWrapper;
-import org.argeo.security.ui.admin.internal.parts.UserEditorInput;
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.handlers.HandlerUtil;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Delete the selected groups */
-public class DeleteGroups extends AbstractHandler {
-       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
-                       + ".deleteGroups";
-
-       /* DEPENDENCY INJECTION */
-       private UserAdminWrapper userAdminWrapper;
-
-       @SuppressWarnings("unchecked")
-       public Object execute(ExecutionEvent event) throws ExecutionException {
-               ISelection selection = HandlerUtil.getCurrentSelection(event);
-               if (selection.isEmpty())
-                       return null;
-
-               List<Group> groups = new ArrayList<Group>();
-               Iterator<Group> it = ((IStructuredSelection) selection).iterator();
-               StringBuilder builder = new StringBuilder();
-               while (it.hasNext()) {
-                       Group currGroup = it.next();
-                       String groupName = UserAdminUtils.getUsername(currGroup);
-                       // TODO add checks
-                       builder.append(groupName).append("; ");
-                       groups.add(currGroup);
-               }
-
-               if (!MessageDialog.openQuestion(HandlerUtil.getActiveShell(event),
-                               "Delete Groups",
-                               "Are you sure that you " + "want to delete these groups?\n"
-                                               + builder.substring(0, builder.length() - 2)))
-                       return null;
-
-               userAdminWrapper.beginTransactionIfNeeded();
-               UserAdmin userAdmin = userAdminWrapper.getUserAdmin();
-               IWorkbenchPage iwp = HandlerUtil.getActiveWorkbenchWindow(event)
-                               .getActivePage();
-               for (Group group : groups) {
-                       String groupName = group.getName();
-                       // TODO find a way to close the editor cleanly if opened. Cannot be
-                       // done through the UserAdminListeners, it causes a
-                       // java.util.ConcurrentModificationException because disposing the
-                       // editor unregisters and disposes the listener
-                       IEditorPart part = iwp.findEditor(new UserEditorInput(groupName));
-                       if (part != null)
-                               iwp.closeEditor(part, false);
-                       userAdmin.removeRole(groupName);
-               }
-               userAdminWrapper.commitOrNotifyTransactionStateChange();
-
-               // Update the view
-               for (Group group : groups) {
-                       userAdminWrapper.notifyListeners(new UserAdminEvent(null,
-                                       UserAdminEvent.ROLE_REMOVED, group));
-               }
-
-               return null;
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/DeleteUsers.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/DeleteUsers.java
deleted file mode 100644 (file)
index e583bef..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.commands;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.argeo.security.ui.admin.SecurityAdminPlugin;
-import org.argeo.security.ui.admin.internal.UserAdminWrapper;
-import org.argeo.security.ui.admin.internal.parts.UserEditorInput;
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.handlers.HandlerUtil;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Delete the selected users */
-public class DeleteUsers extends AbstractHandler {
-       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
-                       + ".deleteUsers";
-
-       /* DEPENDENCY INJECTION */
-       private UserAdminWrapper userAdminWrapper;
-
-       @SuppressWarnings("unchecked")
-       public Object execute(ExecutionEvent event) throws ExecutionException {
-               ISelection selection = HandlerUtil.getCurrentSelection(event);
-               if (selection.isEmpty())
-                       return null;
-
-               Iterator<User> it = ((IStructuredSelection) selection).iterator();
-               List<User> users = new ArrayList<User>();
-               StringBuilder builder = new StringBuilder();
-
-               while (it.hasNext()) {
-                       User currUser = it.next();
-                       String userName = UserAdminUtils.getUsername(currUser);
-                       if (UserAdminUtils.isCurrentUser(currUser)) {
-                               MessageDialog.openError(HandlerUtil.getActiveShell(event),
-                                               "Deletion forbidden",
-                                               "You cannot delete your own user this way.");
-                               return null;
-                       }
-                       builder.append(userName).append("; ");
-                       users.add(currUser);
-               }
-
-               if (!MessageDialog.openQuestion(
-                               HandlerUtil.getActiveShell(event),
-                               "Delete Users",
-                               "Are you sure that you want to delete these users?\n"
-                                               + builder.substring(0, builder.length() - 2)))
-                       return null;
-
-               userAdminWrapper.beginTransactionIfNeeded();
-               UserAdmin userAdmin = userAdminWrapper.getUserAdmin();
-               IWorkbenchPage iwp = HandlerUtil.getActiveWorkbenchWindow(event)
-                               .getActivePage();
-
-               for (User user : users) {
-                       String userName = user.getName();
-                       // TODO find a way to close the editor cleanly if opened. Cannot be
-                       // done through the UserAdminListeners, it causes a
-                       // java.util.ConcurrentModificationException because disposing the
-                       // editor unregisters and disposes the listener
-                       IEditorPart part = iwp.findEditor(new UserEditorInput(userName));
-                       if (part != null)
-                               iwp.closeEditor(part, false);
-                       userAdmin.removeRole(userName);
-               }
-               userAdminWrapper.commitOrNotifyTransactionStateChange();
-
-               for (User user : users) {
-                       userAdminWrapper.notifyListeners(new UserAdminEvent(null,
-                                       UserAdminEvent.ROLE_REMOVED, user));
-               }
-               return null;
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/ForceRefresh.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/ForceRefresh.java
deleted file mode 100644 (file)
index 231c871..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.commands;
-
-import org.argeo.security.ui.admin.internal.parts.GroupsView;
-import org.argeo.security.ui.admin.internal.parts.UsersView;
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.handlers.HandlerUtil;
-
-/** Retrieve the active view or editor and call forceRefresh method if defined */
-public class ForceRefresh extends AbstractHandler {
-
-       public Object execute(ExecutionEvent event) throws ExecutionException {
-               IWorkbenchWindow iww = HandlerUtil.getActiveWorkbenchWindow(event);
-               if (iww == null)
-                       return null;
-               IWorkbenchPage activePage = iww.getActivePage();
-               IWorkbenchPart part = activePage.getActivePart();
-               if (part instanceof UsersView)
-                       ((UsersView) part).refresh();
-               else if (part instanceof GroupsView)
-                       ((GroupsView) part).refresh();
-               return null;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewGroup.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewGroup.java
deleted file mode 100644 (file)
index 75b9b0b..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.commands;
-
-import java.util.Dictionary;
-import java.util.Map;
-
-import org.argeo.cms.CmsException;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.osgi.useradmin.LdifName;
-import org.argeo.osgi.useradmin.UserAdminConf;
-import org.argeo.security.ui.admin.SecurityAdminPlugin;
-import org.argeo.security.ui.admin.internal.UserAdminWrapper;
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.jface.wizard.Wizard;
-import org.eclipse.jface.wizard.WizardDialog;
-import org.eclipse.jface.wizard.WizardPage;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.FocusEvent;
-import org.eclipse.swt.events.FocusListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Combo;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-import org.eclipse.ui.handlers.HandlerUtil;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Create a new group */
-public class NewGroup extends AbstractHandler {
-       public final static String ID = SecurityAdminPlugin.PLUGIN_ID + ".newGroup";
-
-       /* DEPENDENCY INJECTION */
-       private UserAdminWrapper userAdminWrapper;
-
-       public Object execute(ExecutionEvent event) throws ExecutionException {
-               NewGroupWizard newGroupWizard = new NewGroupWizard();
-               newGroupWizard.setWindowTitle("Group creation");
-               WizardDialog dialog = new WizardDialog(
-                               HandlerUtil.getActiveShell(event), newGroupWizard);
-               dialog.open();
-               return null;
-       }
-
-       private class NewGroupWizard extends Wizard {
-
-               // Pages
-               private MainGroupInfoWizardPage mainGroupInfo;
-
-               // UI fields
-               private Text dNameTxt, commonNameTxt, descriptionTxt;
-               private Combo baseDnCmb;
-
-               public NewGroupWizard() {
-               }
-
-               @Override
-               public void addPages() {
-                       mainGroupInfo = new MainGroupInfoWizardPage();
-                       addPage(mainGroupInfo);
-               }
-
-               @SuppressWarnings({ "rawtypes", "unchecked" })
-               @Override
-               public boolean performFinish() {
-                       if (!canFinish())
-                               return false;
-                       String commonName = commonNameTxt.getText();
-                       try {
-                               userAdminWrapper.beginTransactionIfNeeded();
-                               String dn = getDn(commonName);
-                               Group group = (Group) userAdminWrapper.getUserAdmin()
-                                               .createRole(dn, Role.GROUP);
-                               Dictionary props = group.getProperties();
-                               String descStr = descriptionTxt.getText();
-                               if (EclipseUiUtils.notEmpty(descStr))
-                                       props.put(LdifName.description.name(), descStr);
-                               userAdminWrapper.commitOrNotifyTransactionStateChange();
-                               userAdminWrapper.notifyListeners(new UserAdminEvent(null,
-                                               UserAdminEvent.ROLE_CREATED, group));
-                               return true;
-                       } catch (Exception e) {
-                               ErrorFeedback.show("Cannot create new group " + commonName, e);
-                               return false;
-                       }
-               }
-
-               private class MainGroupInfoWizardPage extends WizardPage implements
-                               FocusListener, ArgeoNames {
-                       private static final long serialVersionUID = -3150193365151601807L;
-
-                       public MainGroupInfoWizardPage() {
-                               super("Main");
-                               setTitle("General information");
-                               setMessage("Please choose a domain, provide a common name "
-                                               + "and a free description");
-                       }
-
-                       @Override
-                       public void createControl(Composite parent) {
-                               Composite bodyCmp = new Composite(parent, SWT.NONE);
-                               setControl(bodyCmp);
-                               bodyCmp.setLayout(new GridLayout(2, false));
-
-                               dNameTxt = EclipseUiUtils.createGridLT(bodyCmp,
-                                               "Distinguished name");
-                               dNameTxt.setEnabled(false);
-
-                               baseDnCmb = createGridLC(bodyCmp, "Base DN");
-                               // Initialise before adding the listener to avoid NPE
-                               initialiseDnCmb(baseDnCmb);
-                               baseDnCmb.addFocusListener(this);
-
-                               commonNameTxt = EclipseUiUtils.createGridLT(bodyCmp,
-                                               "Common name");
-                               commonNameTxt.addFocusListener(this);
-
-                               Label descLbl = new Label(bodyCmp, SWT.LEAD);
-                               descLbl.setText("Description");
-                               descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false,
-                                               false));
-                               descriptionTxt = new Text(bodyCmp, SWT.LEAD | SWT.MULTI
-                                               | SWT.WRAP | SWT.BORDER);
-                               descriptionTxt.setLayoutData(EclipseUiUtils.fillAll());
-                               descriptionTxt.addFocusListener(this);
-
-                               // Initialize buttons
-                               setPageComplete(false);
-                               getContainer().updateButtons();
-                       }
-
-                       @Override
-                       public void focusLost(FocusEvent event) {
-                               String name = commonNameTxt.getText();
-                               if (EclipseUiUtils.isEmpty(name))
-                                       dNameTxt.setText("");
-                               else
-                                       dNameTxt.setText(getDn(name));
-
-                               String message = checkComplete();
-                               if (message != null) {
-                                       setMessage(message, WizardPage.ERROR);
-                                       setPageComplete(false);
-                               } else {
-                                       setMessage("Complete", WizardPage.INFORMATION);
-                                       setPageComplete(true);
-                               }
-                               getContainer().updateButtons();
-                       }
-
-                       @Override
-                       public void focusGained(FocusEvent event) {
-                       }
-
-                       /** @return the error message or null if complete */
-                       protected String checkComplete() {
-                               String name = commonNameTxt.getText();
-
-                               if (name.trim().equals(""))
-                                       return "Common name must not be empty";
-                               Role role = userAdminWrapper.getUserAdmin()
-                                               .getRole(getDn(name));
-                               if (role != null)
-                                       return "Group " + name + " already exists";
-                               return null;
-                       }
-
-                       @Override
-                       public void setVisible(boolean visible) {
-                               super.setVisible(visible);
-                               if (visible)
-                                       if (baseDnCmb.getSelectionIndex() == -1)
-                                               baseDnCmb.setFocus();
-                                       else
-                                               commonNameTxt.setFocus();
-                       }
-               }
-
-               private Map<String, String> getDns() {
-                       return userAdminWrapper.getKnownBaseDns(true);
-               }
-
-               private String getDn(String cn) {
-                       Map<String, String> dns = getDns();
-                       String bdn = baseDnCmb.getText();
-                       if (EclipseUiUtils.notEmpty(bdn)) {
-                               Dictionary<String, ?> props = UserAdminConf.uriAsProperties(dns
-                                               .get(bdn));
-                               String dn = LdifName.cn.name() + "=" + cn + ","
-                                               + UserAdminConf.groupBase.getValue(props) + "," + bdn;
-                               return dn;
-                       }
-                       return null;
-               }
-
-               private void initialiseDnCmb(Combo combo) {
-                       Map<String, String> dns = userAdminWrapper.getKnownBaseDns(true);
-                       if (dns.isEmpty())
-                               throw new CmsException(
-                                               "No writable base dn found. Cannot create group");
-                       combo.setItems(dns.keySet().toArray(new String[0]));
-                       if (dns.size() == 1)
-                               combo.select(0);
-               }
-       }
-
-       private Combo createGridLC(Composite parent, String label) {
-               Label lbl = new Label(parent, SWT.LEAD);
-               lbl.setText(label);
-               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
-               Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY);
-               combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-               return combo;
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewUser.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/NewUser.java
deleted file mode 100644 (file)
index c04c835..0000000
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.commands;
-
-import java.util.Dictionary;
-import java.util.List;
-import java.util.Map;
-
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-import javax.naming.ldap.Rdn;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.osgi.useradmin.LdifName;
-import org.argeo.osgi.useradmin.UserAdminConf;
-import org.argeo.security.ui.admin.SecurityAdminPlugin;
-import org.argeo.security.ui.admin.internal.UserAdminWrapper;
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.jface.wizard.Wizard;
-import org.eclipse.jface.wizard.WizardDialog;
-import org.eclipse.jface.wizard.WizardPage;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Combo;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-import org.eclipse.ui.handlers.HandlerUtil;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Open a wizard that enables creation of a new user. */
-public class NewUser extends AbstractHandler {
-       /**
-        * Email addresses must match this regexp pattern ({@value #EMAIL_PATTERN}.
-        * Thanks to <a href=
-        * "http://www.mkyong.com/regular-expressions/how-to-validate-email-address-with-regular-expression/"
-        * >this tip</a>.
-        */
-       public final static String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
-       // private final static Log log = LogFactory.getLog(NewUser.class);
-       public final static String ID = SecurityAdminPlugin.PLUGIN_ID + ".newUser";
-
-       /* DEPENDENCY INJECTION */
-       private UserAdminWrapper userAdminWrapper;
-
-       public Object execute(ExecutionEvent event) throws ExecutionException {
-               NewUserWizard newUserWizard = new NewUserWizard();
-               newUserWizard.setWindowTitle("User creation");
-               WizardDialog dialog = new WizardDialog(
-                               HandlerUtil.getActiveShell(event), newUserWizard);
-               dialog.open();
-               return null;
-       }
-
-       private class NewUserWizard extends Wizard {
-
-               // pages
-               private MainUserInfoWizardPage mainUserInfo;
-
-               // End user fields
-               private Text dNameTxt, usernameTxt, firstNameTxt, lastNameTxt,
-                               primaryMailTxt, pwd1Txt, pwd2Txt;
-               private Combo baseDnCmb;
-
-               public NewUserWizard() {
-
-               }
-
-               @Override
-               public void addPages() {
-                       mainUserInfo = new MainUserInfoWizardPage();
-                       addPage(mainUserInfo);
-                       String message = "Default wizard that also eases user creation tests:\n "
-                                       + "Mail and last name are automatically "
-                                       + "generated form the uid. Password are defauted to 'demo'.";
-                       mainUserInfo.setMessage(message, WizardPage.WARNING);
-               }
-
-               @SuppressWarnings({ "rawtypes", "unchecked" })
-               @Override
-               public boolean performFinish() {
-                       if (!canFinish())
-                               return false;
-                       String username = mainUserInfo.getUsername();
-                       userAdminWrapper.beginTransactionIfNeeded();
-                       try {
-                               User user = (User) userAdminWrapper.getUserAdmin().createRole(
-                                               getDn(username), Role.USER);
-
-                               Dictionary props = user.getProperties();
-
-                               String lastNameStr = lastNameTxt.getText();
-                               if (EclipseUiUtils.notEmpty(lastNameStr))
-                                       props.put(LdifName.sn.name(), lastNameStr);
-
-                               String firstNameStr = firstNameTxt.getText();
-                               if (EclipseUiUtils.notEmpty(firstNameStr))
-                                       props.put(LdifName.givenName.name(), firstNameStr);
-
-                               String cn = UserAdminUtils.buildDefaultCn(firstNameStr,
-                                               lastNameStr);
-                               if (EclipseUiUtils.notEmpty(cn))
-                                       props.put(LdifName.cn.name(), cn);
-
-                               String mailStr = primaryMailTxt.getText();
-                               if (EclipseUiUtils.notEmpty(mailStr))
-                                       props.put(LdifName.mail.name(), mailStr);
-
-                               char[] password = mainUserInfo.getPassword();
-                               user.getCredentials().put(null, password);
-                               userAdminWrapper.commitOrNotifyTransactionStateChange();
-                               userAdminWrapper.notifyListeners(new UserAdminEvent(null,
-                                               UserAdminEvent.ROLE_CREATED, user));
-                               return true;
-                       } catch (Exception e) {
-                               ErrorFeedback.show("Cannot create new user " + username, e);
-                               return false;
-                       }
-               }
-
-               private class MainUserInfoWizardPage extends WizardPage implements
-                               ModifyListener, ArgeoNames {
-                       private static final long serialVersionUID = -3150193365151601807L;
-
-                       public MainUserInfoWizardPage() {
-                               super("Main");
-                               setTitle("Required Information");
-                       }
-
-                       @Override
-                       public void createControl(Composite parent) {
-                               Composite composite = new Composite(parent, SWT.NONE);
-                               composite.setLayout(new GridLayout(2, false));
-                               dNameTxt = EclipseUiUtils.createGridLT(composite,
-                                               "Distinguished name", this);
-                               dNameTxt.setEnabled(false);
-
-                               baseDnCmb = createGridLC(composite, "Base DN");
-                               initialiseDnCmb(baseDnCmb);
-                               baseDnCmb.addModifyListener(this);
-                               baseDnCmb.addModifyListener(new ModifyListener() {
-                                       private static final long serialVersionUID = -1435351236582736843L;
-
-                                       @Override
-                                       public void modifyText(ModifyEvent event) {
-                                               String name = usernameTxt.getText();
-                                               dNameTxt.setText(getDn(name));
-                                       }
-                               });
-
-                               usernameTxt = EclipseUiUtils.createGridLT(composite,
-                                               "Local ID", this);
-                               usernameTxt.addModifyListener(new ModifyListener() {
-                                       private static final long serialVersionUID = -1435351236582736843L;
-
-                                       @Override
-                                       public void modifyText(ModifyEvent event) {
-                                               String name = usernameTxt.getText();
-                                               if (name.trim().equals("")) {
-                                                       dNameTxt.setText("");
-                                                       lastNameTxt.setText("");
-                                                       primaryMailTxt.setText("");
-                                                       pwd1Txt.setText("");
-                                                       pwd2Txt.setText("");
-                                               } else {
-                                                       dNameTxt.setText(getDn(name));
-                                                       lastNameTxt.setText(name.toUpperCase());
-                                                       primaryMailTxt.setText(getMail(name));
-                                                       pwd1Txt.setText("demo");
-                                                       pwd2Txt.setText("demo");
-                                               }
-                                       }
-                               });
-
-                               primaryMailTxt = EclipseUiUtils.createGridLT(composite,
-                                               "Email", this);
-                               firstNameTxt = EclipseUiUtils.createGridLT(composite,
-                                               "First name", this);
-                               lastNameTxt = EclipseUiUtils.createGridLT(composite,
-                                               "Last name", this);
-                               pwd1Txt = EclipseUiUtils.createGridLP(composite, "Password",
-                                               this);
-                               pwd2Txt = EclipseUiUtils.createGridLP(composite,
-                                               "Repeat password", this);
-                               setControl(composite);
-
-                               // Initialize buttons
-                               setPageComplete(false);
-                               getContainer().updateButtons();
-                       }
-
-                       @Override
-                       public void modifyText(ModifyEvent event) {
-                               String message = checkComplete();
-                               if (message != null) {
-                                       setMessage(message, WizardPage.ERROR);
-                                       setPageComplete(false);
-                               } else {
-                                       setMessage("Complete", WizardPage.INFORMATION);
-                                       setPageComplete(true);
-                               }
-                               getContainer().updateButtons();
-                       }
-
-                       /** @return error message or null if complete */
-                       protected String checkComplete() {
-                               String name = usernameTxt.getText();
-
-                               if (name.trim().equals(""))
-                                       return "User name must not be empty";
-                               Role role = userAdminWrapper.getUserAdmin()
-                                               .getRole(getDn(name));
-                               if (role != null)
-                                       return "User " + name + " already exists";
-                               if (!primaryMailTxt.getText().matches(EMAIL_PATTERN))
-                                       return "Not a valid email address";
-                               if (lastNameTxt.getText().trim().equals(""))
-                                       return "Specify a last name";
-                               if (pwd1Txt.getText().trim().equals(""))
-                                       return "Specify a password";
-                               if (pwd2Txt.getText().trim().equals(""))
-                                       return "Repeat the password";
-                               if (!pwd2Txt.getText().equals(pwd1Txt.getText()))
-                                       return "Passwords are different";
-                               return null;
-                       }
-
-                       @Override
-                       public void setVisible(boolean visible) {
-                               super.setVisible(visible);
-                               if (visible)
-                                       if (baseDnCmb.getSelectionIndex() == -1)
-                                               baseDnCmb.setFocus();
-                                       else
-                                               usernameTxt.setFocus();
-                       }
-
-                       public String getUsername() {
-                               return usernameTxt.getText();
-                       }
-
-                       public char[] getPassword() {
-                               return pwd1Txt.getTextChars();
-                       }
-
-               }
-
-               private Map<String, String> getDns() {
-                       return userAdminWrapper.getKnownBaseDns(true);
-               }
-
-               private String getDn(String uid) {
-                       Map<String, String> dns = getDns();
-                       String bdn = baseDnCmb.getText();
-                       if (EclipseUiUtils.notEmpty(bdn)) {
-                               Dictionary<String, ?> props = UserAdminConf.uriAsProperties(dns
-                                               .get(bdn));
-                               String dn = LdifName.uid.name() + "=" + uid + ","
-                                               + UserAdminConf.userBase.getValue(props) + "," + bdn;
-                               return dn;
-                       }
-                       return null;
-               }
-
-               private void initialiseDnCmb(Combo combo) {
-                       Map<String, String> dns = userAdminWrapper.getKnownBaseDns(true);
-                       if (dns.isEmpty())
-                               throw new CmsException(
-                                               "No writable base dn found. Cannot create user");
-                       combo.setItems(dns.keySet().toArray(new String[0]));
-                       if (dns.size() == 1)
-                               combo.select(0);
-               }
-
-               private String getMail(String username) {
-                       if (baseDnCmb.getSelectionIndex() == -1)
-                               return null;
-                       String baseDn = baseDnCmb.getText();
-                       try {
-                               LdapName name = new LdapName(baseDn);
-                               List<Rdn> rdns = name.getRdns();
-                               return username + "@" + (String) rdns.get(1).getValue() + '.'
-                                               + (String) rdns.get(0).getValue();
-                       } catch (InvalidNameException e) {
-                               throw new CmsException("Unable to generate mail for "
-                                               + username + " with base dn " + baseDn, e);
-                       }
-               }
-       }
-
-       private Combo createGridLC(Composite parent, String label) {
-               Label lbl = new Label(parent, SWT.LEAD);
-               lbl.setText(label);
-               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
-               Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY);
-               combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-               return combo;
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/SaveArgeoUser.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/SaveArgeoUser.java
deleted file mode 100644 (file)
index 61d8a7d..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.commands;
-
-import org.argeo.security.ui.admin.SecurityAdminPlugin;
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.handlers.HandlerUtil;
-
-/** Save the currently edited Argeo user. */
-public class SaveArgeoUser extends AbstractHandler {
-       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
-                       + ".saveArgeoUser";
-
-       public Object execute(ExecutionEvent event) throws ExecutionException {
-               try {
-                       IWorkbenchPart iwp = HandlerUtil.getActiveWorkbenchWindow(event)
-                                       .getActivePage().getActivePart();
-                       if (!(iwp instanceof IEditorPart))
-                               return null;
-                       IEditorPart editor = (IEditorPart) iwp;
-                       editor.doSave(null);
-               } catch (Exception e) {
-                       MessageDialog.openError(Display.getDefault().getActiveShell(),
-                                       "Error", "Cannot save user: " + e.getMessage());
-               }
-               return null;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/UserBatchUpdate.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/UserBatchUpdate.java
deleted file mode 100644 (file)
index c02f5c2..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.commands;
-
-import org.argeo.security.ui.admin.internal.UserAdminWrapper;
-import org.argeo.security.ui.admin.internal.parts.UserBatchUpdateWizard;
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.jface.wizard.WizardDialog;
-import org.eclipse.ui.handlers.HandlerUtil;
-
-/** Launch a wizard to perform batch process on users */
-public class UserBatchUpdate extends AbstractHandler {
-
-       /* DEPENDENCY INJECTION */
-       private UserAdminWrapper uaWrapper;
-
-       public Object execute(ExecutionEvent event) throws ExecutionException {
-               UserBatchUpdateWizard wizard = new UserBatchUpdateWizard(uaWrapper);
-               wizard.setWindowTitle("User batch processing");
-               WizardDialog dialog = new WizardDialog(
-                               HandlerUtil.getActiveShell(event), wizard);
-               dialog.open();
-               return null;
-       }
-
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.uaWrapper = userAdminWrapper;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/UserTransactionHandler.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/commands/UserTransactionHandler.java
deleted file mode 100644 (file)
index 236584c..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.commands;
-
-import javax.transaction.Status;
-import javax.transaction.UserTransaction;
-
-import org.argeo.cms.CmsException;
-import org.argeo.security.ui.admin.SecurityAdminPlugin;
-import org.argeo.security.ui.admin.internal.UiAdminUtils;
-import org.argeo.security.ui.admin.internal.UserAdminWrapper;
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Manage the transaction that is bound to the current perspective */
-public class UserTransactionHandler extends AbstractHandler {
-       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
-                       + ".userTransactionHandler";
-
-       public final static String PARAM_COMMAND_ID = "param.commandId";
-
-       public final static String TRANSACTION_BEGIN = "transaction.begin";
-       public final static String TRANSACTION_COMMIT = "transaction.commit";
-       public final static String TRANSACTION_ROLLBACK = "transaction.rollback";
-
-       /* DEPENDENCY INJECTION */
-       private UserAdminWrapper userAdminWrapper;
-
-       public Object execute(ExecutionEvent event) throws ExecutionException {
-               String commandId = event.getParameter(PARAM_COMMAND_ID);
-               final UserTransaction userTransaction = userAdminWrapper
-                               .getUserTransaction();
-               try {
-                       if (TRANSACTION_BEGIN.equals(commandId)) {
-                               if (userTransaction.getStatus() != Status.STATUS_NO_TRANSACTION)
-                                       throw new CmsException("A transaction already exists");
-                               else
-                                       userTransaction.begin();
-                       } else if (TRANSACTION_COMMIT.equals(commandId)) {
-                               if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION)
-                                       throw new CmsException("No transaction.");
-                               else
-                                       userTransaction.commit();
-                       } else if (TRANSACTION_ROLLBACK.equals(commandId)) {
-                               if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION)
-                                       throw new CmsException("No transaction to rollback.");
-                               else {
-                                       userTransaction.rollback();
-                                       userAdminWrapper.notifyListeners(new UserAdminEvent(null,
-                                                       UserAdminEvent.ROLE_CHANGED, null));
-                               }
-                       }
-
-                       UiAdminUtils.notifyTransactionStateChange(userTransaction);
-                       // Try to remove invalid thread access errors when managing users.
-                       // HandlerUtil.getActivePart(event).getSite().getShell().getDisplay()
-                       // .asyncExec(new Runnable() {
-                       // @Override
-                       // public void run() {
-                       // UiAdminUtils
-                       // .notifyTransactionStateChange(userTransaction);
-                       // }
-                       // });
-
-               } catch (CmsException e) {
-                       throw e;
-               } catch (Exception e) {
-                       throw new CmsException("Unable to call " + commandId + " on "
-                                       + userTransaction, e);
-               }
-               return null;
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/ArgeoUserEditorInput.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/ArgeoUserEditorInput.java
deleted file mode 100644 (file)
index cca2a12..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.parts;
-
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.ui.IEditorInput;
-import org.eclipse.ui.IPersistableElement;
-
-/** Editor input for an Argeo user. */
-public class ArgeoUserEditorInput implements IEditorInput {
-       private final String username;
-
-       public ArgeoUserEditorInput(String username) {
-               this.username = username;
-       }
-
-       public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
-               return null;
-       }
-
-       public boolean exists() {
-               return username != null;
-       }
-
-       public ImageDescriptor getImageDescriptor() {
-               return null;
-       }
-
-       public String getName() {
-               return username != null ? username : "<new user>";
-       }
-
-       public IPersistableElement getPersistable() {
-               return null;
-       }
-
-       public String getToolTipText() {
-               return username != null ? username : "<new user>";
-       }
-
-       public boolean equals(Object obj) {
-               if (!(obj instanceof ArgeoUserEditorInput))
-                       return false;
-               if (((ArgeoUserEditorInput) obj).getUsername() == null)
-                       return false;
-               return ((ArgeoUserEditorInput) obj).getUsername().equals(username);
-       }
-
-       public String getUsername() {
-               return username;
-       }
-}
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/DefaultUserMainPage.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/DefaultUserMainPage.java
deleted file mode 100644 (file)
index 6529efe..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.parts;
-
-import java.util.Arrays;
-
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.argeo.jcr.ArgeoNames;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-import org.eclipse.ui.forms.AbstractFormPart;
-import org.eclipse.ui.forms.IManagedForm;
-import org.eclipse.ui.forms.SectionPart;
-import org.eclipse.ui.forms.editor.FormEditor;
-import org.eclipse.ui.forms.editor.FormPage;
-import org.eclipse.ui.forms.widgets.FormToolkit;
-import org.eclipse.ui.forms.widgets.ScrolledForm;
-import org.eclipse.ui.forms.widgets.Section;
-
-/** Display/edit the properties common to all Argeo users */
-public class DefaultUserMainPage extends FormPage implements ArgeoNames {
-       final static String ID = "argeoUserEditor.mainPage";
-
-       private final static Log log = LogFactory.getLog(DefaultUserMainPage.class);
-       private Node userProfile;
-
-       private char[] newPassword;
-
-       public DefaultUserMainPage(FormEditor editor, Node userProfile) {
-               super(editor, ID, "Main");
-               this.userProfile = userProfile;
-       }
-
-       protected void createFormContent(final IManagedForm mf) {
-               try {
-                       ScrolledForm form = mf.getForm();
-                       refreshFormTitle(form);
-                       GridLayout mainLayout = new GridLayout(1, true);
-                       form.getBody().setLayout(mainLayout);
-
-                       createGeneralPart(form.getBody());
-                       createPassworPart(form.getBody());
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot create form content", e);
-               }
-       }
-
-       /** Creates the general section */
-       protected void createGeneralPart(Composite parent)
-                       throws RepositoryException {
-               FormToolkit tk = getManagedForm().getToolkit();
-               Section section = tk.createSection(parent, Section.TITLE_BAR);
-               section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
-               section.setText("General");
-               Composite body = tk.createComposite(section, SWT.WRAP);
-               section.setClient(body);
-               GridLayout layout = new GridLayout(2, false);
-               body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
-               body.setLayout(layout);
-
-               final Text commonName = createLT(body, "Displayed Name",
-                               getProperty(Property.JCR_TITLE));
-               final Text firstName = createLT(body, "First name",
-                               getProperty(ARGEO_FIRST_NAME));
-               final Text lastName = createLT(body, "Last name",
-                               getProperty(ARGEO_LAST_NAME));
-               final Text email = createLT(body, "Email",
-                               getProperty(ARGEO_PRIMARY_EMAIL));
-               final Text description = createLMT(body, "Description",
-                               getProperty(Property.JCR_DESCRIPTION));
-
-               // create form part (controller)
-               AbstractFormPart part = new SectionPart(section) {
-                       public void commit(boolean onSave) {
-                               try {
-                                       userProfile.getSession().getWorkspace().getVersionManager()
-                                                       .checkout(userProfile.getPath());
-                                       userProfile.setProperty(Property.JCR_TITLE,
-                                                       commonName.getText());
-                                       userProfile.setProperty(ARGEO_FIRST_NAME,
-                                                       firstName.getText());
-                                       userProfile
-                                                       .setProperty(ARGEO_LAST_NAME, lastName.getText());
-                                       userProfile.setProperty(ARGEO_PRIMARY_EMAIL,
-                                                       email.getText());
-                                       userProfile.setProperty(Property.JCR_DESCRIPTION,
-                                                       description.getText());
-                                       userProfile.getSession().save();
-                                       userProfile.getSession().getWorkspace().getVersionManager()
-                                                       .checkin(userProfile.getPath());
-                                       super.commit(onSave);
-                                       refreshFormTitle(getManagedForm().getForm());
-                                       if (log.isTraceEnabled())
-                                               log.trace("General part committed");
-                               } catch (RepositoryException e) {
-                                       throw new CmsException("Cannot commit", e);
-                               }
-                       }
-               };
-               // if (username != null)
-               // username.addModifyListener(new FormPartML(part));
-               commonName.addModifyListener(new FormPartML(part));
-               firstName.addModifyListener(new FormPartML(part));
-               lastName.addModifyListener(new FormPartML(part));
-               email.addModifyListener(new FormPartML(part));
-               description.addModifyListener(new FormPartML(part));
-               getManagedForm().addPart(part);
-       }
-
-       private void refreshFormTitle(ScrolledForm form) throws RepositoryException {
-               form.setText(getProperty(Property.JCR_TITLE)
-                               + (userProfile.getProperty(ARGEO_ENABLED).getBoolean() ? ""
-                                               : " [DISABLED]"));
-       }
-
-       /** @return the property, or the empty string if not set */
-       protected String getProperty(String name) throws RepositoryException {
-               return userProfile.hasProperty(name) ? userProfile.getProperty(name)
-                               .getString() : "";
-       }
-
-       /** Creates the password section */
-       protected void createPassworPart(Composite parent) {
-               FormToolkit tk = getManagedForm().getToolkit();
-               Section section = tk.createSection(parent, Section.TITLE_BAR);
-               section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
-               section.setText("Password");
-
-               Composite body = tk.createComposite(section, SWT.WRAP);
-               section.setClient(body);
-               GridLayout layout = new GridLayout(2, false);
-               body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
-               body.setLayout(layout);
-
-               // add widgets (view)
-               final Text password1 = createLP(body, "New password", "");
-               final Text password2 = createLP(body, "Repeat password", "");
-               // create form part (controller)
-               AbstractFormPart part = new SectionPart(section) {
-
-                       public void commit(boolean onSave) {
-                               if (!password1.getText().equals("")
-                                               || !password2.getText().equals("")) {
-                                       if (password1.getText().equals(password2.getText())) {
-                                               newPassword = password1.getText().toCharArray();
-                                               password1.setText("");
-                                               password2.setText("");
-                                               super.commit(onSave);
-                                       } else {
-                                               password1.setText("");
-                                               password2.setText("");
-                                               throw new CmsException("Passwords are not equals");
-                                       }
-                               }
-                       }
-
-               };
-               password1.addModifyListener(new FormPartML(part));
-               password2.addModifyListener(new FormPartML(part));
-               getManagedForm().addPart(part);
-       }
-
-       /** Creates label and text. */
-       protected Text createLT(Composite body, String label, String value) {
-               FormToolkit toolkit = getManagedForm().getToolkit();
-               Label lbl = toolkit.createLabel(body, label);
-               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
-               Text text = toolkit.createText(body, value, SWT.BORDER);
-               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-               return text;
-       }
-
-       /** Creates label and multiline text. */
-       protected Text createLMT(Composite body, String label, String value) {
-               FormToolkit toolkit = getManagedForm().getToolkit();
-               Label lbl = toolkit.createLabel(body, label);
-               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
-               Text text = toolkit.createText(body, value, SWT.BORDER | SWT.MULTI);
-               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
-               return text;
-       }
-
-       /** Creates label and password. */
-       protected Text createLP(Composite body, String label, String value) {
-               FormToolkit toolkit = getManagedForm().getToolkit();
-               Label lbl = toolkit.createLabel(body, label);
-               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
-               Text text = toolkit.createText(body, value, SWT.BORDER | SWT.PASSWORD);
-               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-               return text;
-       }
-
-       private class FormPartML implements ModifyListener {
-               private static final long serialVersionUID = 6299808129505381333L;
-               private AbstractFormPart formPart;
-
-               public FormPartML(AbstractFormPart generalPart) {
-                       this.formPart = generalPart;
-               }
-
-               public void modifyText(ModifyEvent e) {
-                       formPart.markDirty();
-               }
-       }
-
-       public String getNewPassword() {
-               if (newPassword != null)
-                       return new String(newPassword);
-               else
-                       return null;
-       }
-
-       public void resetNewPassword() {
-               if (newPassword != null)
-                       Arrays.fill(newPassword, 'x');
-               newPassword = null;
-       }
-}
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/GroupMainPage.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/GroupMainPage.java
deleted file mode 100644 (file)
index fe56679..0000000
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.parts;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.transaction.UserTransaction;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.parts.LdifUsersTable;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.osgi.useradmin.LdifName;
-import org.argeo.security.ui.admin.SecurityAdminImages;
-import org.argeo.security.ui.admin.internal.UserAdminWrapper;
-import org.argeo.security.ui.admin.internal.parts.UserEditor.GroupChangeListener;
-import org.argeo.security.ui.admin.internal.parts.UserEditor.MainInfoListener;
-import org.argeo.security.ui.admin.internal.providers.CommonNameLP;
-import org.argeo.security.ui.admin.internal.providers.MailLP;
-import org.argeo.security.ui.admin.internal.providers.RoleIconLP;
-import org.argeo.security.ui.admin.internal.providers.UserFilter;
-import org.argeo.security.ui.admin.internal.providers.UserNameLP;
-import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener;
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.action.ToolBarManager;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.ViewerDropAdapter;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DropTargetEvent;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.dnd.TransferData;
-import org.eclipse.swt.events.DisposeEvent;
-import org.eclipse.swt.events.DisposeListener;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.graphics.Cursor;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-import org.eclipse.swt.widgets.ToolBar;
-import org.eclipse.ui.forms.AbstractFormPart;
-import org.eclipse.ui.forms.IManagedForm;
-import org.eclipse.ui.forms.SectionPart;
-import org.eclipse.ui.forms.editor.FormEditor;
-import org.eclipse.ui.forms.editor.FormPage;
-import org.eclipse.ui.forms.widgets.FormToolkit;
-import org.eclipse.ui.forms.widgets.ScrolledForm;
-import org.eclipse.ui.forms.widgets.Section;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Display/edit main properties of a given group */
-public class GroupMainPage extends FormPage implements ArgeoNames {
-       final static String ID = "GroupEditor.mainPage";
-
-       private final UserEditor editor;
-       private UserAdminWrapper userAdminWrapper;
-
-       // Local configuration
-       private final int PRE_TITLE_INDENT = 10;
-
-       public GroupMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) {
-               super(editor, ID, "Main");
-               this.editor = (UserEditor) editor;
-               this.userAdminWrapper = userAdminWrapper;
-       }
-
-       protected void createFormContent(final IManagedForm mf) {
-               ScrolledForm form = mf.getForm();
-               Composite body = form.getBody();
-               GridLayout mainLayout = new GridLayout();
-               body.setLayout(mainLayout);
-               Group group = (Group) editor.getDisplayedUser();
-               appendOverviewPart(body, group);
-               appendMembersPart(body, group);
-       }
-
-       /** Creates the general section */
-       protected void appendOverviewPart(final Composite parent, final Group group) {
-               FormToolkit tk = getManagedForm().getToolkit();
-               Composite body = addSection(tk, parent, "Main information");
-               GridLayout layout = new GridLayout(2, false);
-               body.setLayout(layout);
-
-               final Text dnTxt = createLT(body, "DN", group.getName());
-               dnTxt.setEnabled(false);
-
-               final Text cnTxt = createLT(body, "Common Name",
-                               UserAdminUtils.getProperty(group, LdifName.cn.name()));
-               cnTxt.setEnabled(false);
-
-               Label descLbl = new Label(body, SWT.LEAD);
-               descLbl.setText("Description");
-               descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));
-               final Text descTxt = new Text(body, SWT.LEAD | SWT.MULTI | SWT.WRAP
-                               | SWT.BORDER);
-               GridData gd = EclipseUiUtils.fillAll();
-               gd.heightHint = 100;
-               descTxt.setLayoutData(gd);
-
-               // create form part (controller)
-               AbstractFormPart part = new SectionPart((Section) body.getParent()) {
-
-                       private MainInfoListener listener;
-
-                       @Override
-                       public void initialize(IManagedForm form) {
-                               super.initialize(form);
-                               listener = editor.new MainInfoListener(parent.getDisplay(),
-                                               this);
-                               userAdminWrapper.addListener(listener);
-                       }
-
-                       @Override
-                       public void dispose() {
-                               userAdminWrapper.removeListener(listener);
-                               super.dispose();
-                       }
-
-                       @SuppressWarnings("unchecked")
-                       public void commit(boolean onSave) {
-                               group.getProperties().put(LdifName.description.name(),
-                                               descTxt.getText());
-                               // Enable common name ?
-                               // editor.setProperty(UserAdminConstants.KEY_CN,
-                               // email.getText());
-                               super.commit(onSave);
-                       }
-
-                       @Override
-                       public void refresh() {
-                               refreshFormTitle(group);
-                               dnTxt.setText(group.getName());
-                               cnTxt.setText(UserAdminUtils.getProperty(group,
-                                               LdifName.cn.name()));
-                               descTxt.setText(UserAdminUtils.getProperty(group,
-                                               LdifName.description.name()));
-                               super.refresh();
-                       }
-               };
-
-               ModifyListener defaultListener = editor.new FormPartML(part);
-               descTxt.addModifyListener(defaultListener);
-               getManagedForm().addPart(part);
-       }
-
-       /** Filtered table with members. Has drag & drop ability */
-       protected void appendMembersPart(Composite parent, Group group) {
-
-               FormToolkit tk = getManagedForm().getToolkit();
-               Section section = tk.createSection(parent, Section.TITLE_BAR);
-               section.setLayoutData(EclipseUiUtils.fillAll());
-
-               Composite body = new Composite(section, SWT.NO_FOCUS);
-               section.setClient(body);
-               body.setLayoutData(EclipseUiUtils.fillAll());
-
-               LdifUsersTable userTableViewerCmp = createMemberPart(body, group);
-
-               SectionPart part = new GroupMembersPart(section, userTableViewerCmp,
-                               group);
-               getManagedForm().addPart(part);
-               addRemoveAbitily(part, userTableViewerCmp.getTableViewer(), group);
-       }
-
-       public LdifUsersTable createMemberPart(Composite parent, Group group) {
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-               // Define the displayed columns
-               List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-               columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24));
-               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name",
-                               150));
-               columnDefs.add(new ColumnDefinition(new MailLP(), "Primary Mail", 150));
-               columnDefs.add(new ColumnDefinition(new UserNameLP(),
-                               "Distinguished Name", 240));
-
-               // Create and configure the table
-               LdifUsersTable userViewerCmp = new MyUserTableViewer(parent, SWT.MULTI
-                               | SWT.H_SCROLL | SWT.V_SCROLL, userAdminWrapper.getUserAdmin());
-
-               userViewerCmp.setColumnDefinitions(columnDefs);
-               userViewerCmp.populate(true, false);
-               userViewerCmp.setLayoutData(EclipseUiUtils.fillAll());
-
-               // Controllers
-               TableViewer userViewer = userViewerCmp.getTableViewer();
-               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener());
-               int operations = DND.DROP_COPY | DND.DROP_MOVE;
-               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
-               userViewer.addDropSupport(operations, tt,
-                               new GroupDropListener(userAdminWrapper, userViewerCmp,
-                                               (Group) editor.getDisplayedUser()));
-
-               return userViewerCmp;
-       }
-
-       // Local viewers
-       private class MyUserTableViewer extends LdifUsersTable {
-               private static final long serialVersionUID = 8467999509931900367L;
-
-               private final UserFilter userFilter;
-
-               public MyUserTableViewer(Composite parent, int style,
-                               UserAdmin userAdmin) {
-                       super(parent, style, true);
-                       userFilter = new UserFilter();
-
-               }
-
-               @Override
-               protected List<User> listFilteredElements(String filter) {
-                       Group group = (Group) editor.getDisplayedUser();
-                       Role[] roles = group.getMembers();
-                       List<User> users = new ArrayList<User>();
-                       userFilter.setSearchText(filter);
-                       for (Role role : roles)
-                               // if (role.getType() == Role.GROUP)
-                               if (userFilter.select(null, null, role))
-                                       users.add((User) role);
-                       return users;
-               }
-       }
-
-       private void addRemoveAbitily(SectionPart sectionPart,
-                       TableViewer userViewer, Group group) {
-               Section section = sectionPart.getSection();
-               ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
-               ToolBar toolbar = toolBarManager.createControl(section);
-               final Cursor handCursor = new Cursor(section.getDisplay(),
-                               SWT.CURSOR_HAND);
-               toolbar.setCursor(handCursor);
-               toolbar.addDisposeListener(new DisposeListener() {
-                       private static final long serialVersionUID = 3882131405820522925L;
-
-                       public void widgetDisposed(DisposeEvent e) {
-                               if ((handCursor != null) && (handCursor.isDisposed() == false)) {
-                                       handCursor.dispose();
-                               }
-                       }
-               });
-
-               Action action = new RemoveMembershipAction(userViewer, group,
-                               "Remove selected items from this group",
-                               SecurityAdminImages.ICON_REMOVE_DESC);
-               toolBarManager.add(action);
-               toolBarManager.update(true);
-               section.setTextClient(toolbar);
-       }
-
-       private class RemoveMembershipAction extends Action {
-               private static final long serialVersionUID = -1337713097184522588L;
-
-               private final TableViewer userViewer;
-               private final Group group;
-
-               RemoveMembershipAction(TableViewer userViewer, Group group,
-                               String name, ImageDescriptor img) {
-                       super(name, img);
-                       this.userViewer = userViewer;
-                       this.group = group;
-               }
-
-               @Override
-               public void run() {
-                       ISelection selection = userViewer.getSelection();
-                       if (selection.isEmpty())
-                               return;
-
-                       @SuppressWarnings("unchecked")
-                       Iterator<User> it = ((IStructuredSelection) selection).iterator();
-                       List<User> users = new ArrayList<User>();
-                       while (it.hasNext()) {
-                               User currUser = it.next();
-                               users.add(currUser);
-                       }
-
-                       userAdminWrapper.beginTransactionIfNeeded();
-                       for (User user : users) {
-                               group.removeMember(user);
-                       }
-                       userAdminWrapper.commitOrNotifyTransactionStateChange();
-                       userAdminWrapper.notifyListeners(new UserAdminEvent(null,
-                                       UserAdminEvent.ROLE_CHANGED, group));
-               }
-       }
-
-       // LOCAL CONTROLLERS
-       private class GroupMembersPart extends SectionPart {
-               private final LdifUsersTable userViewer;
-               private final Group group;
-
-               private GroupChangeListener listener;
-
-               public GroupMembersPart(Section section, LdifUsersTable userViewer,
-                               Group group) {
-                       super(section);
-                       this.userViewer = userViewer;
-                       this.group = group;
-               }
-
-               @Override
-               public void initialize(IManagedForm form) {
-                       super.initialize(form);
-                       listener = editor.new GroupChangeListener(userViewer.getDisplay(),
-                                       GroupMembersPart.this);
-                       userAdminWrapper.addListener(listener);
-               }
-
-               @Override
-               public void dispose() {
-                       userAdminWrapper.removeListener(listener);
-                       super.dispose();
-               }
-
-               @Override
-               public void refresh() {
-                       refreshFormTitle(group);
-                       getSection().setText(
-                                       "Members of group "
-                                                       + UserAdminUtils.getProperty(group,
-                                                                       LdifName.cn.name()));
-                       userViewer.refresh();
-                       super.refresh();
-               }
-       }
-
-       /**
-        * Defines this table as being a potential target to add group membership
-        * (roles) to this group
-        */
-       private class GroupDropListener extends ViewerDropAdapter {
-               private static final long serialVersionUID = 2893468717831451621L;
-
-               private final UserAdminWrapper userAdminWrapper;
-               // private final LdifUsersTable myUserViewerCmp;
-               private final Group myGroup;
-
-               public GroupDropListener(UserAdminWrapper userAdminWrapper,
-                               LdifUsersTable userTableViewerCmp, Group group) {
-                       super(userTableViewerCmp.getTableViewer());
-                       this.userAdminWrapper = userAdminWrapper;
-                       this.myGroup = group;
-                       // this.myUserViewerCmp = userTableViewerCmp;
-               }
-
-               @Override
-               public boolean validateDrop(Object target, int operation,
-                               TransferData transferType) {
-                       // Target is always OK in a list only view
-                       // TODO check if not a string
-                       boolean validDrop = true;
-                       return validDrop;
-               }
-
-               @Override
-               public void drop(DropTargetEvent event) {
-                       // TODO Is there an opportunity to perform the check before?
-
-                       String newUserName = (String) event.data;
-                       UserAdmin myUserAdmin = userAdminWrapper.getUserAdmin();
-                       Role role = myUserAdmin.getRole(newUserName);
-                       if (role.getType() == Role.GROUP) {
-                               Group newGroup = (Group) role;
-                               Shell shell = getViewer().getControl().getShell();
-                               // Sanity checks
-                               if (myGroup == newGroup) { // Equality
-                                       MessageDialog.openError(shell, "Forbidden addition ",
-                                                       "A group cannot be a member of itself.");
-                                       return;
-                               }
-
-                               // Cycle
-                               String myName = myGroup.getName();
-                               List<User> myMemberships = editor.getFlatGroups(myGroup);
-                               if (myMemberships.contains(newGroup)) {
-                                       MessageDialog.openError(shell, "Forbidden addition: cycle",
-                                                       "Cannot add " + newUserName + " to group " + myName
-                                                                       + ". This would create a cycle");
-                                       return;
-                               }
-
-                               // Already member
-                               List<User> newGroupMemberships = editor.getFlatGroups(newGroup);
-                               if (newGroupMemberships.contains(myGroup)) {
-                                       MessageDialog.openError(shell, "Forbidden addition",
-                                                       "Cannot add " + newUserName + " to group " + myName
-                                                                       + ", this membership already exists");
-                                       return;
-                               }
-                               userAdminWrapper.beginTransactionIfNeeded();
-                               myGroup.addMember(newGroup);
-                               userAdminWrapper.commitOrNotifyTransactionStateChange();
-                               userAdminWrapper.notifyListeners(new UserAdminEvent(null,
-                                               UserAdminEvent.ROLE_CHANGED, myGroup));
-                       } else if (role.getType() == Role.USER) {
-                               // TODO check if the group is already member of this group
-                               UserTransaction transaction = userAdminWrapper
-                                               .beginTransactionIfNeeded();
-                               User user = (User) role;
-                               myGroup.addMember(user);
-                               if (UserAdminWrapper.COMMIT_ON_SAVE)
-                                       try {
-                                               transaction.commit();
-                                       } catch (Exception e) {
-                                               throw new CmsException("Cannot commit transaction "
-                                                               + "after user group membership update", e);
-                                       }
-                               userAdminWrapper.notifyListeners(new UserAdminEvent(null,
-                                               UserAdminEvent.ROLE_CHANGED, myGroup));
-                       }
-                       super.drop(event);
-               }
-
-               @Override
-               public boolean performDrop(Object data) {
-                       // myUserViewerCmp.refresh();
-                       return true;
-               }
-       }
-
-       // LOCAL HELPERS
-       private void refreshFormTitle(Group group) {
-               getManagedForm().getForm().setText(
-                               UserAdminUtils.getProperty(group, LdifName.cn.name()));
-       }
-
-       private Composite addSection(FormToolkit tk, Composite parent, String title) {
-               Section section = tk.createSection(parent, Section.TITLE_BAR);
-               GridData gd = EclipseUiUtils.fillWidth();
-               gd.verticalAlignment = PRE_TITLE_INDENT;
-               section.setLayoutData(gd);
-               section.setText(title);
-               Composite body = tk.createComposite(section, SWT.WRAP);
-               body.setLayoutData(EclipseUiUtils.fillAll());
-               section.setClient(body);
-               return body;
-       }
-
-       /** Creates label and text. */
-       private Text createLT(Composite body, String label, String value) {
-               FormToolkit toolkit = getManagedForm().getToolkit();
-               Label lbl = toolkit.createLabel(body, label);
-               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
-               Text text = toolkit.createText(body, value, SWT.BORDER);
-               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-               return text;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/GroupsView.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/GroupsView.java
deleted file mode 100644 (file)
index 82f4e1b..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.parts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.parts.LdifUsersTable;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.osgi.useradmin.LdifName;
-import org.argeo.security.ui.admin.SecurityAdminPlugin;
-import org.argeo.security.ui.admin.internal.UiUserAdminListener;
-import org.argeo.security.ui.admin.internal.UserAdminWrapper;
-import org.argeo.security.ui.admin.internal.providers.CommonNameLP;
-import org.argeo.security.ui.admin.internal.providers.DomainNameLP;
-import org.argeo.security.ui.admin.internal.providers.RoleIconLP;
-import org.argeo.security.ui.admin.internal.providers.UserDragListener;
-import org.argeo.security.ui.admin.internal.providers.UserNameLP;
-import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.part.ViewPart;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdminEvent;
-import org.osgi.service.useradmin.UserAdminListener;
-
-/** List all groups with filter */
-public class GroupsView extends ViewPart implements ArgeoNames {
-       private final static Log log = LogFactory.getLog(GroupsView.class);
-       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
-                       + ".groupsView";
-
-       /* DEPENDENCY INJECTION */
-       private UserAdminWrapper userAdminWrapper;
-
-       // UI Objects
-       private LdifUsersTable groupTableViewerCmp;
-       private TableViewer userViewer;
-       private List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-
-       private UserAdminListener listener;
-
-       @Override
-       public void createPartControl(Composite parent) {
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-               boolean isAdmin = UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN);
-
-               // Define the displayed columns
-               columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 26));
-               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name",
-                               150));
-               columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
-               // Only show technical DN to admin
-               if (isAdmin)
-                       columnDefs.add(new ColumnDefinition(new UserNameLP(),
-                                       "Distinguished Name", 300));
-
-               // Create and configure the table
-               groupTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI
-                               | SWT.H_SCROLL | SWT.V_SCROLL);
-
-               groupTableViewerCmp.setColumnDefinitions(columnDefs);
-               if (isAdmin)
-                       groupTableViewerCmp.populateWithStaticFilters(false, false);
-               else
-                       groupTableViewerCmp.populate(true, false);
-
-               groupTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll());
-
-               // Links
-               userViewer = groupTableViewerCmp.getTableViewer();
-               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener());
-               getViewSite().setSelectionProvider(userViewer);
-
-               // Really?
-               groupTableViewerCmp.refresh();
-
-               // Drag and drop
-               int operations = DND.DROP_COPY | DND.DROP_MOVE;
-               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
-               userViewer.addDragSupport(operations, tt, new UserDragListener(
-                               userViewer));
-
-               // // Register a useradmin listener
-               // listener = new UserAdminListener() {
-               // @Override
-               // public void roleChanged(UserAdminEvent event) {
-               // if (userViewer != null && !userViewer.getTable().isDisposed())
-               // refresh();
-               // }
-               // };
-               // userAdminWrapper.addListener(listener);
-               // }
-
-               // Register a useradmin listener
-               listener = new MyUiUAListener(parent.getDisplay());
-               userAdminWrapper.addListener(listener);
-       }
-
-       private class MyUiUAListener extends UiUserAdminListener {
-               public MyUiUAListener(Display display) {
-                       super(display);
-               }
-
-               @Override
-               public void roleChangedToUiThread(UserAdminEvent event) {
-                       if (userViewer != null && !userViewer.getTable().isDisposed())
-                               refresh();
-               }
-       }
-
-       private class MyUserTableViewer extends LdifUsersTable {
-               private static final long serialVersionUID = 8467999509931900367L;
-
-               private boolean showSystemRoles = false;
-
-               private final String[] knownProps = { LdifName.uid.name(),
-                               LdifName.cn.name(), LdifName.dn.name() };
-
-               public MyUserTableViewer(Composite parent, int style) {
-                       super(parent, style);
-               }
-
-               protected void populateStaticFilters(Composite staticFilterCmp) {
-                       staticFilterCmp.setLayout(new GridLayout());
-                       final Button showSystemRoleBtn = new Button(staticFilterCmp,
-                                       SWT.CHECK);
-                       showSystemRoleBtn.setText("Show system roles");
-                       showSystemRoleBtn.addSelectionListener(new SelectionAdapter() {
-                               private static final long serialVersionUID = -7033424592697691676L;
-
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       showSystemRoles = showSystemRoleBtn.getSelection();
-                                       refresh();
-                               }
-
-                       });
-               }
-
-               @Override
-               protected List<User> listFilteredElements(String filter) {
-                       Role[] roles;
-                       try {
-                               StringBuilder builder = new StringBuilder();
-                               StringBuilder tmpBuilder = new StringBuilder();
-                               if (EclipseUiUtils.notEmpty(filter))
-                                       for (String prop : knownProps) {
-                                               tmpBuilder.append("(");
-                                               tmpBuilder.append(prop);
-                                               tmpBuilder.append("=*");
-                                               tmpBuilder.append(filter);
-                                               tmpBuilder.append("*)");
-                                       }
-                               if (tmpBuilder.length() > 1) {
-                                       builder.append("(&(").append(LdifName.objectClass.name())
-                                                       .append("=").append(LdifName.groupOfNames.name())
-                                                       .append(")");
-                                       if (!showSystemRoles)
-                                               builder.append("(!(").append(LdifName.dn.name())
-                                                               .append("=*")
-                                                               .append(AuthConstants.ROLES_BASEDN)
-                                                               .append("))");
-                                       builder.append("(|");
-                                       builder.append(tmpBuilder.toString());
-                                       builder.append("))");
-                               } else {
-                                       if (!showSystemRoles)
-                                               builder.append("(&(")
-                                                               .append(LdifName.objectClass.name())
-                                                               .append("=")
-                                                               .append(LdifName.groupOfNames.name())
-                                                               .append(")(!(").append(LdifName.dn.name())
-                                                               .append("=*")
-                                                               .append(AuthConstants.ROLES_BASEDN)
-                                                               .append(")))");
-                                       else
-                                               builder.append("(").append(LdifName.objectClass.name())
-                                                               .append("=")
-                                                               .append(LdifName.groupOfNames.name())
-                                                               .append(")");
-
-                               }
-                               roles = userAdminWrapper.getUserAdmin().getRoles(
-                                               builder.toString());
-                       } catch (InvalidSyntaxException e) {
-                               throw new CmsException("Unable to get roles with filter: "
-                                               + filter, e);
-                       }
-                       List<User> users = new ArrayList<User>();
-                       for (Role role : roles)
-                               if (!users.contains(role))
-                                       users.add((User) role);
-                               else
-                                       log.warn("Duplicated role: " + role);
-
-                       return users;
-               }
-       }
-
-       public void refresh() {
-               groupTableViewerCmp.refresh();
-       }
-
-       // Override generic view methods
-       @Override
-       public void dispose() {
-               userAdminWrapper.removeListener(listener);
-               super.dispose();
-       }
-
-       @Override
-       public void setFocus() {
-               groupTableViewerCmp.setFocus();
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserBatchUpdateWizard.java
deleted file mode 100644 (file)
index 31b2042..0000000
+++ /dev/null
@@ -1,578 +0,0 @@
-package org.argeo.security.ui.admin.internal.parts;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.transaction.SystemException;
-import javax.transaction.UserTransaction;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.parts.LdifUsersTable;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.osgi.useradmin.LdifName;
-import org.argeo.security.ui.admin.internal.UserAdminWrapper;
-import org.argeo.security.ui.admin.internal.providers.CommonNameLP;
-import org.argeo.security.ui.admin.internal.providers.DomainNameLP;
-import org.argeo.security.ui.admin.internal.providers.MailLP;
-import org.argeo.security.ui.admin.internal.providers.UserNameLP;
-import org.eclipse.jface.dialogs.IPageChangeProvider;
-import org.eclipse.jface.dialogs.IPageChangedListener;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.dialogs.PageChangedEvent;
-import org.eclipse.jface.wizard.IWizardContainer;
-import org.eclipse.jface.wizard.Wizard;
-import org.eclipse.jface.wizard.WizardPage;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Combo;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Text;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-
-/** Wizard to update users */
-public class UserBatchUpdateWizard extends Wizard {
-
-       private final static Log log = LogFactory
-                       .getLog(UserBatchUpdateWizard.class);
-       private UserAdminWrapper userAdminWrapper;
-
-       // pages
-       private ChooseCommandWizardPage chooseCommandPage;
-       private ChooseUsersWizardPage userListPage;
-       private ValidateAndLaunchWizardPage validatePage;
-
-       // Various implemented commands keys
-       private final static String CMD_UPDATE_PASSWORD = "resetPassword";
-       private final static String CMD_GROUP_MEMBERSHIP = "groupMembership";
-
-       private final Map<String, String> commands = new HashMap<String, String>() {
-               private static final long serialVersionUID = 1L;
-               {
-                       put("Reset password(s)", CMD_UPDATE_PASSWORD);
-                       // TODO implement role / group management
-                       // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP);
-               }
-       };
-
-       public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-
-       @Override
-       public void addPages() {
-               chooseCommandPage = new ChooseCommandWizardPage();
-               addPage(chooseCommandPage);
-               userListPage = new ChooseUsersWizardPage();
-               addPage(userListPage);
-               validatePage = new ValidateAndLaunchWizardPage();
-               addPage(validatePage);
-       }
-
-       @Override
-       public boolean performFinish() {
-               if (!canFinish())
-                       return false;
-               UserTransaction ut = userAdminWrapper.getUserTransaction();
-               try {
-                       if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION
-                                       && !MessageDialog.openConfirm(getShell(),
-                                                       "Existing Transaction",
-                                                       "A user transaction is already existing, "
-                                                                       + "are you sure you want to proceed ?"))
-                               return false;
-               } catch (SystemException e) {
-                       throw new CmsException("Cannot get user transaction state "
-                                       + "before user batch update", e);
-               }
-
-               // We cannot use jobs, user modifications are still meant to be done in
-               // the UIThread
-               // UpdateJob job = null;
-               // if (job != null)
-               // job.schedule();
-
-               if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) {
-                       char[] newValue = chooseCommandPage.getPwdValue();
-                       if (newValue == null)
-                               throw new CmsException(
-                                               "Password cannot be null or an empty string");
-                       ResetPassword job = new ResetPassword(userAdminWrapper,
-                                       userListPage.getSelectedUsers(), newValue);
-                       job.doUpdate();
-               }
-               return true;
-       }
-
-       public boolean canFinish() {
-               if (this.getContainer().getCurrentPage() == validatePage)
-                       return true;
-               return false;
-       }
-
-       private class ResetPassword {
-               private char[] newPwd;
-               private UserAdminWrapper userAdminWrapper;
-               private List<User> usersToUpdate;
-
-               public ResetPassword(UserAdminWrapper userAdminWrapper,
-                               List<User> usersToUpdate, char[] newPwd) {
-                       this.newPwd = newPwd;
-                       this.usersToUpdate = usersToUpdate;
-                       this.userAdminWrapper = userAdminWrapper;
-               }
-
-               @SuppressWarnings("unchecked")
-               protected void doUpdate() {
-                       userAdminWrapper.beginTransactionIfNeeded();
-                       try {
-                               for (User user : usersToUpdate) {
-                                       // the char array is emptied after being used.
-                                       user.getCredentials().put(null, newPwd.clone());
-                               }
-                               userAdminWrapper.commitOrNotifyTransactionStateChange();
-                       } catch (Exception e) {
-                               throw new CmsException("Cannot perform batch update on users",
-                                               e);
-                       } finally {
-                               UserTransaction ut = userAdminWrapper.getUserTransaction();
-                               try {
-                                       if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
-                                               ut.rollback();
-                               } catch (IllegalStateException | SecurityException
-                                               | SystemException e) {
-                                       log.error("Unable to rollback session in 'finally', "
-                                                       + "the system might be in a dirty state");
-                                       e.printStackTrace();
-                               }
-                       }
-               }
-       }
-
-       // @SuppressWarnings("unused")
-       // private class AddToGroup extends UpdateJob {
-       // private String groupID;
-       // private Session session;
-       //
-       // public AddToGroup(Session session, List<Node> nodesToUpdate,
-       // String groupID) {
-       // super(session, nodesToUpdate);
-       // this.session = session;
-       // this.groupID = groupID;
-       // }
-       //
-       // protected void doUpdate(Node node) {
-       // log.info("Add/Remove to group actions are not yet implemented");
-       // // TODO implement this
-       // // try {
-       // // throw new CmsException("Not yet implemented");
-       // // } catch (RepositoryException re) {
-       // // throw new CmsException(
-       // // "Unable to update boolean value for node " + node, re);
-       // // }
-       // }
-       // }
-
-       // /**
-       // * Base privileged job that will be run asynchronously to perform the
-       // batch
-       // * update
-       // */
-       // private abstract class UpdateJob extends PrivilegedJob {
-       //
-       // private final UserAdminWrapper userAdminWrapper;
-       // private final List<User> usersToUpdate;
-       //
-       // protected abstract void doUpdate(User user);
-       //
-       // public UpdateJob(UserAdminWrapper userAdminWrapper,
-       // List<User> usersToUpdate) {
-       // super("Perform update");
-       // this.usersToUpdate = usersToUpdate;
-       // this.userAdminWrapper = userAdminWrapper;
-       // }
-       //
-       // @Override
-       // protected IStatus doRun(IProgressMonitor progressMonitor) {
-       // try {
-       // ArgeoMonitor monitor = new EclipseArgeoMonitor(progressMonitor);
-       // int total = usersToUpdate.size();
-       // monitor.beginTask("Performing change", total);
-       // userAdminWrapper.beginTransactionIfNeeded();
-       // for (User user : usersToUpdate) {
-       // doUpdate(user);
-       // monitor.worked(1);
-       // }
-       // userAdminWrapper.getUserTransaction().commit();
-       // } catch (Exception e) {
-       // throw new CmsException(
-       // "Cannot perform batch update on users", e);
-       // } finally {
-       // UserTransaction ut = userAdminWrapper.getUserTransaction();
-       // try {
-       // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
-       // ut.rollback();
-       // } catch (IllegalStateException | SecurityException
-       // | SystemException e) {
-       // log.error("Unable to rollback session in 'finally', "
-       // + "the system might be in a dirty state");
-       // e.printStackTrace();
-       // }
-       // }
-       // return Status.OK_STATUS;
-       // }
-       // }
-
-       // PAGES
-       /** Displays a combo box that enables user to choose which action to perform */
-       private class ChooseCommandWizardPage extends WizardPage {
-               private static final long serialVersionUID = -8069434295293996633L;
-               private Combo chooseCommandCmb;
-               private Button trueChk;
-               private Text valueTxt;
-               private Text pwdTxt;
-               private Text pwd2Txt;
-
-               public ChooseCommandWizardPage() {
-                       super("Choose a command to run.");
-                       setTitle("Choose a command to run.");
-               }
-
-               @Override
-               public void createControl(Composite parent) {
-                       GridLayout gl = new GridLayout();
-                       Composite container = new Composite(parent, SWT.NO_FOCUS);
-                       container.setLayout(gl);
-
-                       chooseCommandCmb = new Combo(container, SWT.READ_ONLY);
-                       chooseCommandCmb.setLayoutData(EclipseUiUtils.fillWidth());
-                       String[] values = commands.keySet().toArray(new String[0]);
-                       chooseCommandCmb.setItems(values);
-
-                       final Composite bottomPart = new Composite(container, SWT.NO_FOCUS);
-                       bottomPart.setLayoutData(EclipseUiUtils.fillAll());
-                       bottomPart.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-                       chooseCommandCmb.addSelectionListener(new SelectionAdapter() {
-                               private static final long serialVersionUID = 1L;
-
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       if (getCommand().equals(CMD_UPDATE_PASSWORD))
-                                               populatePasswordCmp(bottomPart);
-                                       else if (getCommand().equals(CMD_GROUP_MEMBERSHIP))
-                                               populateGroupCmp(bottomPart);
-                                       else
-                                               populateBooleanFlagCmp(bottomPart);
-                                       checkPageComplete();
-                                       bottomPart.layout(true, true);
-                               }
-                       });
-                       setControl(container);
-               }
-
-               private void populateBooleanFlagCmp(Composite parent) {
-                       EclipseUiUtils.clear(parent);
-                       trueChk = new Button(parent, SWT.CHECK);
-                       trueChk.setText("Do it. (It will to the contrary if unchecked)");
-                       trueChk.setSelection(true);
-                       trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
-               }
-
-               private void populatePasswordCmp(Composite parent) {
-                       EclipseUiUtils.clear(parent);
-                       Composite body = new Composite(parent, SWT.NO_FOCUS);
-
-                       ModifyListener ml = new ModifyListener() {
-                               private static final long serialVersionUID = -1558726363536729634L;
-
-                               @Override
-                               public void modifyText(ModifyEvent event) {
-                                       checkPageComplete();
-                               }
-                       };
-
-                       body.setLayout(new GridLayout(2, false));
-                       body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-                       pwdTxt = EclipseUiUtils.createGridLP(body, "New password", ml);
-                       pwd2Txt = EclipseUiUtils.createGridLP(body, "Repeat password", ml);
-               }
-
-               private void checkPageComplete() {
-                       String errorMsg = null;
-                       if (chooseCommandCmb.getSelectionIndex() < 0)
-                               errorMsg = "Please select an action";
-                       else if (CMD_UPDATE_PASSWORD.equals(getCommand())) {
-                               if (EclipseUiUtils.isEmpty(pwdTxt.getText())
-                                               || pwdTxt.getText().length() < 4)
-                                       errorMsg = "Please enter a password that is at least 4 character long";
-                               else if (!pwdTxt.getText().equals(pwd2Txt.getText()))
-                                       errorMsg = "Passwords are different";
-                       }
-                       if (EclipseUiUtils.notEmpty(errorMsg)) {
-                               setMessage(errorMsg, WizardPage.ERROR);
-                               setPageComplete(false);
-                       } else {
-                               setMessage("Page complete, you can proceed to user choice",
-                                               WizardPage.INFORMATION);
-                               setPageComplete(true);
-                       }
-
-                       getContainer().updateButtons();
-               }
-
-               private void populateGroupCmp(Composite parent) {
-                       EclipseUiUtils.clear(parent);
-                       trueChk = new Button(parent, SWT.CHECK);
-                       trueChk.setText("Add to group. (It will remove user(s) from the "
-                                       + "corresponding group if unchecked)");
-                       trueChk.setSelection(true);
-                       trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
-               }
-
-               protected String getCommand() {
-                       return commands.get(chooseCommandCmb.getItem(chooseCommandCmb
-                                       .getSelectionIndex()));
-               }
-
-               protected String getCommandLbl() {
-                       return chooseCommandCmb.getItem(chooseCommandCmb
-                                       .getSelectionIndex());
-               }
-
-               @SuppressWarnings("unused")
-               protected boolean getBoleanValue() {
-                       // FIXME this is not consistent and will lead to errors.
-                       if (ArgeoNames.ARGEO_ENABLED.equals(getCommand()))
-                               return trueChk.getSelection();
-                       else
-                               return !trueChk.getSelection();
-               }
-
-               @SuppressWarnings("unused")
-               protected String getStringValue() {
-                       String value = null;
-                       if (valueTxt != null) {
-                               value = valueTxt.getText();
-                               if ("".equals(value.trim()))
-                                       value = null;
-                       }
-                       return value;
-               }
-
-               protected char[] getPwdValue() {
-                       // We do not directly reset the password text fields: There is no
-                       // need to over secure this process: setting a pwd to multi users
-                       // at the same time is anyhow a bad practice and should be used only
-                       // in test environment or for temporary access
-                       if (pwdTxt == null || pwdTxt.isDisposed())
-                               return null;
-                       else
-                               return pwdTxt.getText().toCharArray();
-               }
-       }
-
-       /**
-        * Displays a list of users with a check box to be able to choose some of
-        * them
-        */
-       private class ChooseUsersWizardPage extends WizardPage implements
-                       IPageChangedListener {
-               private static final long serialVersionUID = 7651807402211214274L;
-               private ChooseUserTableViewer userTableCmp;
-
-               public ChooseUsersWizardPage() {
-                       super("Choose Users");
-                       setTitle("Select users who will be impacted");
-               }
-
-               @Override
-               public void createControl(Composite parent) {
-                       Composite pageCmp = new Composite(parent, SWT.NONE);
-                       pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-                       // Define the displayed columns
-                       List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-                       columnDefs.add(new ColumnDefinition(new CommonNameLP(),
-                                       "Common Name", 150));
-                       columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
-                       columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain",
-                                       200));
-
-                       // Only show technical DN to admin
-                       if (UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN))
-                               columnDefs.add(new ColumnDefinition(new UserNameLP(),
-                                               "Distinguished Name", 300));
-
-                       userTableCmp = new ChooseUserTableViewer(pageCmp, SWT.MULTI
-                                       | SWT.H_SCROLL | SWT.V_SCROLL);
-                       userTableCmp.setLayoutData(EclipseUiUtils.fillAll());
-                       userTableCmp.setColumnDefinitions(columnDefs);
-                       userTableCmp.populate(true, true);
-                       userTableCmp.refresh();
-
-                       setControl(pageCmp);
-
-                       // Add listener to update message when shown
-                       final IWizardContainer wContainer = this.getContainer();
-                       if (wContainer instanceof IPageChangeProvider) {
-                               ((IPageChangeProvider) wContainer).addPageChangedListener(this);
-                       }
-
-               }
-
-               @Override
-               public void pageChanged(PageChangedEvent event) {
-                       if (event.getSelectedPage() == this) {
-                               String msg = "Chosen batch action: "
-                                               + chooseCommandPage.getCommandLbl();
-                               ((WizardPage) event.getSelectedPage()).setMessage(msg);
-                       }
-               }
-
-               protected List<User> getSelectedUsers() {
-                       return userTableCmp.getSelectedUsers();
-               }
-
-               private class ChooseUserTableViewer extends LdifUsersTable {
-                       private static final long serialVersionUID = 5080437561015853124L;
-                       private final String[] knownProps = { LdifName.uid.name(),
-                                       LdifName.dn.name(), LdifName.cn.name(),
-                                       LdifName.givenName.name(), LdifName.sn.name(),
-                                       LdifName.mail.name() };
-
-                       public ChooseUserTableViewer(Composite parent, int style) {
-                               super(parent, style);
-                       }
-
-                       @Override
-                       protected List<User> listFilteredElements(String filter) {
-                               Role[] roles;
-
-                               try {
-                                       StringBuilder builder = new StringBuilder();
-
-                                       StringBuilder tmpBuilder = new StringBuilder();
-                                       if (EclipseUiUtils.notEmpty(filter))
-                                               for (String prop : knownProps) {
-                                                       tmpBuilder.append("(");
-                                                       tmpBuilder.append(prop);
-                                                       tmpBuilder.append("=*");
-                                                       tmpBuilder.append(filter);
-                                                       tmpBuilder.append("*)");
-                                               }
-                                       if (tmpBuilder.length() > 1) {
-                                               builder.append("(&(")
-                                                               .append(LdifName.objectClass.name())
-                                                               .append("=")
-                                                               .append(LdifName.inetOrgPerson.name())
-                                                               .append(")(|");
-                                               builder.append(tmpBuilder.toString());
-                                               builder.append("))");
-                                       } else
-                                               builder.append("(").append(LdifName.objectClass.name())
-                                                               .append("=")
-                                                               .append(LdifName.inetOrgPerson.name())
-                                                               .append(")");
-                                       roles = userAdminWrapper.getUserAdmin().getRoles(
-                                                       builder.toString());
-                               } catch (InvalidSyntaxException e) {
-                                       throw new CmsException("Unable to get roles with filter: "
-                                                       + filter, e);
-                               }
-                               List<User> users = new ArrayList<User>();
-                               for (Role role : roles)
-                                       // Prevent current logged in user to perform batch on
-                                       // himself
-                                       if (!UserAdminUtils.isCurrentUser((User) role))
-                                               users.add((User) role);
-                               return users;
-                       }
-               }
-       }
-
-       /** Summary of input data before launching the process */
-       private class ValidateAndLaunchWizardPage extends WizardPage implements
-                       IPageChangedListener {
-               private static final long serialVersionUID = 7098918351451743853L;
-               private ChosenUsersTableViewer userTableCmp;
-
-               public ValidateAndLaunchWizardPage() {
-                       super("Validate and launch");
-                       setTitle("Validate and launch");
-               }
-
-               @Override
-               public void createControl(Composite parent) {
-                       Composite pageCmp = new Composite(parent, SWT.NO_FOCUS);
-                       pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-                       List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-                       columnDefs.add(new ColumnDefinition(new CommonNameLP(),
-                                       "Common Name", 150));
-                       columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
-                       columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain",
-                                       200));
-                       // Only show technical DN to admin
-                       if (UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN))
-                               columnDefs.add(new ColumnDefinition(new UserNameLP(),
-                                               "Distinguished Name", 300));
-                       userTableCmp = new ChosenUsersTableViewer(pageCmp, SWT.MULTI
-                                       | SWT.H_SCROLL | SWT.V_SCROLL);
-                       userTableCmp.setLayoutData(EclipseUiUtils.fillAll());
-                       userTableCmp.setColumnDefinitions(columnDefs);
-                       userTableCmp.populate(false, false);
-                       userTableCmp.refresh();
-                       setControl(pageCmp);
-                       // Add listener to update message when shown
-                       final IWizardContainer wContainer = this.getContainer();
-                       if (wContainer instanceof IPageChangeProvider) {
-                               ((IPageChangeProvider) wContainer).addPageChangedListener(this);
-                       }
-               }
-
-               @Override
-               public void pageChanged(PageChangedEvent event) {
-                       if (event.getSelectedPage() == this) {
-                               @SuppressWarnings({ "unchecked", "rawtypes" })
-                               Object[] values = ((ArrayList) userListPage.getSelectedUsers())
-                                               .toArray(new Object[userListPage.getSelectedUsers()
-                                                               .size()]);
-                               userTableCmp.getTableViewer().setInput(values);
-                               String msg = "Following batch action: ["
-                                               + chooseCommandPage.getCommandLbl()
-                                               + "] will be perfomed on the users listed below.\n";
-                               // + "Are you sure you want to proceed?";
-                               setMessage(msg);
-                       }
-               }
-
-               private class ChosenUsersTableViewer extends LdifUsersTable {
-                       private static final long serialVersionUID = 7814764735794270541L;
-
-                       public ChosenUsersTableViewer(Composite parent, int style) {
-                               super(parent, style);
-                       }
-
-                       @Override
-                       protected List<User> listFilteredElements(String filter) {
-                               return userListPage.getSelectedUsers();
-                       }
-               }
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserEditor.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserEditor.java
deleted file mode 100644 (file)
index 6c0731d..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.parts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.argeo.osgi.useradmin.LdifName;
-import org.argeo.security.ui.admin.SecurityAdminPlugin;
-import org.argeo.security.ui.admin.internal.UiUserAdminListener;
-import org.argeo.security.ui.admin.internal.UserAdminWrapper;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.IEditorInput;
-import org.eclipse.ui.IEditorSite;
-import org.eclipse.ui.PartInitException;
-import org.eclipse.ui.forms.AbstractFormPart;
-import org.eclipse.ui.forms.editor.FormEditor;
-import org.osgi.service.useradmin.Authorization;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Editor for a user, might be a user or a group. */
-public class UserEditor extends FormEditor {
-       private static final long serialVersionUID = 8357851520380820241L;
-
-       public final static String USER_EDITOR_ID = SecurityAdminPlugin.PLUGIN_ID
-                       + ".userEditor";
-       public final static String GROUP_EDITOR_ID = SecurityAdminPlugin.PLUGIN_ID
-                       + ".groupEditor";
-
-       /* DEPENDENCY INJECTION */
-       private UserAdminWrapper userAdminWrapper;
-       private UserAdmin userAdmin;
-
-       // Context
-       private User user;
-       private String username;
-
-       private NameChangeListener listener;
-
-       public void init(IEditorSite site, IEditorInput input)
-                       throws PartInitException {
-               super.init(site, input);
-               username = ((UserEditorInput) getEditorInput()).getUsername();
-               user = (User) userAdmin.getRole(username);
-
-               listener = new NameChangeListener(site.getShell().getDisplay(), user);
-               userAdminWrapper.addListener(listener);
-               updateEditorTitle(null);
-       }
-
-       /**
-        * returns the list of all authorization for the given user or of the
-        * current displayed user if parameter is null
-        */
-       protected List<User> getFlatGroups(User aUser) {
-               Authorization currAuth;
-               if (aUser == null)
-                       currAuth = userAdmin.getAuthorization(this.user);
-               else
-                       currAuth = userAdmin.getAuthorization(aUser);
-
-               String[] roles = currAuth.getRoles();
-
-               List<User> groups = new ArrayList<User>();
-               for (String roleStr : roles) {
-                       User currRole = (User) userAdmin.getRole(roleStr);
-                       if (!groups.contains(currRole))
-                               groups.add(currRole);
-               }
-               return groups;
-       }
-
-       /** Exposes the user (or group) that is displayed by the current editor */
-       protected User getDisplayedUser() {
-               return user;
-       }
-
-       void updateEditorTitle(String title) {
-               if (title == null) {
-                       String commonName = UserAdminUtils.getProperty(user,
-                                       LdifName.cn.name());
-                       title = "".equals(commonName) ? user.getName() : commonName;
-               }
-               setPartName(title);
-       }
-
-       protected void addPages() {
-               try {
-                       if (user.getType() == Role.GROUP)
-                               addPage(new GroupMainPage(this, userAdminWrapper));
-                       else
-                               addPage(new UserMainPage(this, userAdminWrapper));
-               } catch (Exception e) {
-                       throw new CmsException("Cannot add pages", e);
-               }
-       }
-
-       @Override
-       public void doSave(IProgressMonitor monitor) {
-               userAdminWrapper.beginTransactionIfNeeded();
-               commitPages(true);
-               userAdminWrapper.commitOrNotifyTransactionStateChange();
-               firePropertyChange(PROP_DIRTY);
-               userAdminWrapper.notifyListeners(new UserAdminEvent(null,
-                               UserAdminEvent.ROLE_REMOVED, user));
-       }
-
-       @Override
-       public void doSaveAs() {
-       }
-
-       @Override
-       public boolean isSaveAsAllowed() {
-               return false;
-       }
-
-       @Override
-       public void dispose() {
-               userAdminWrapper.removeListener(listener);
-               super.dispose();
-       }
-
-       // CONTROLERS FOR THIS EDITOR AND ITS PAGES
-
-       private class NameChangeListener extends UiUserAdminListener {
-
-               private final User user;
-
-               public NameChangeListener(Display display, User user) {
-                       super(display);
-                       this.user = user;
-               }
-
-               @Override
-               public void roleChangedToUiThread(UserAdminEvent event) {
-                       Role changedRole = event.getRole();
-                       if (changedRole == null || changedRole.equals(user))
-                               updateEditorTitle(null);
-               }
-       }
-
-       class MainInfoListener extends UiUserAdminListener {
-               private final AbstractFormPart part;
-
-               public MainInfoListener(Display display, AbstractFormPart part) {
-                       super(display);
-                       this.part = part;
-               }
-
-               @Override
-               public void roleChangedToUiThread(UserAdminEvent event) {
-                       // Rollback
-                       if (event.getRole() == null)
-                               part.markStale();
-               }
-       }
-
-       class GroupChangeListener extends UiUserAdminListener {
-               private final AbstractFormPart part;
-
-               public GroupChangeListener(Display display, AbstractFormPart part) {
-                       super(display);
-                       this.part = part;
-               }
-
-               @Override
-               public void roleChangedToUiThread(UserAdminEvent event) {
-                       // always mark as stale
-                       part.markStale();
-               }
-       }
-
-       /** Registers a listener that will notify this part */
-       class FormPartML implements ModifyListener {
-               private static final long serialVersionUID = 6299808129505381333L;
-               private AbstractFormPart formPart;
-
-               public FormPartML(AbstractFormPart generalPart) {
-                       this.formPart = generalPart;
-               }
-
-               public void modifyText(ModifyEvent e) {
-                       // Discard event when the control does not have the focus, typically
-                       // to avoid all editors being marked as dirty during a Rollback
-                       if (((Control) e.widget).isFocusControl())
-                               formPart.markDirty();
-               }
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-               this.userAdmin = userAdminWrapper.getUserAdmin();
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserEditorInput.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserEditorInput.java
deleted file mode 100644 (file)
index fe129f8..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.parts;
-
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.ui.IEditorInput;
-import org.eclipse.ui.IPersistableElement;
-
-/**
- * Editor input for an user defined by unique name (usually a distinguished
- * name).
- */
-public class UserEditorInput implements IEditorInput {
-       private final String username;
-
-       public UserEditorInput(String username) {
-               this.username = username;
-       }
-
-       public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
-               return null;
-       }
-
-       public boolean exists() {
-               return username != null;
-       }
-
-       public ImageDescriptor getImageDescriptor() {
-               return null;
-       }
-
-       public String getName() {
-               return username != null ? username : "<new user>";
-       }
-
-       public IPersistableElement getPersistable() {
-               return null;
-       }
-
-       public String getToolTipText() {
-               return username != null ? username : "<new user>";
-       }
-
-       public boolean equals(Object obj) {
-               if (!(obj instanceof UserEditorInput))
-                       return false;
-               if (((UserEditorInput) obj).getUsername() == null)
-                       return false;
-               return ((UserEditorInput) obj).getUsername().equals(username);
-       }
-
-       public String getUsername() {
-               return username;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UserMainPage.java
deleted file mode 100644 (file)
index db6eb53..0000000
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.parts;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.parts.LdifUsersTable;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.osgi.useradmin.LdifName;
-import org.argeo.security.ui.admin.SecurityAdminImages;
-import org.argeo.security.ui.admin.internal.UserAdminWrapper;
-import org.argeo.security.ui.admin.internal.parts.UserEditor.GroupChangeListener;
-import org.argeo.security.ui.admin.internal.parts.UserEditor.MainInfoListener;
-import org.argeo.security.ui.admin.internal.providers.CommonNameLP;
-import org.argeo.security.ui.admin.internal.providers.DomainNameLP;
-import org.argeo.security.ui.admin.internal.providers.RoleIconLP;
-import org.argeo.security.ui.admin.internal.providers.UserFilter;
-import org.argeo.security.ui.admin.internal.providers.UserNameLP;
-import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener;
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.action.ToolBarManager;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.jface.viewers.ViewerDropAdapter;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DropTargetEvent;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.dnd.TransferData;
-import org.eclipse.swt.events.DisposeEvent;
-import org.eclipse.swt.events.DisposeListener;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Cursor;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-import org.eclipse.swt.widgets.ToolBar;
-import org.eclipse.ui.forms.AbstractFormPart;
-import org.eclipse.ui.forms.IManagedForm;
-import org.eclipse.ui.forms.SectionPart;
-import org.eclipse.ui.forms.editor.FormEditor;
-import org.eclipse.ui.forms.editor.FormPage;
-import org.eclipse.ui.forms.widgets.FormToolkit;
-import org.eclipse.ui.forms.widgets.ScrolledForm;
-import org.eclipse.ui.forms.widgets.Section;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Display/edit the properties of a given user */
-public class UserMainPage extends FormPage implements ArgeoNames {
-       final static String ID = "UserEditor.mainPage";
-
-       private final UserEditor editor;
-       private UserAdminWrapper userAdminWrapper;
-
-       // Local configuration
-       private final int PRE_TITLE_INDENT = 10;
-
-       public UserMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) {
-               super(editor, ID, "Main");
-               this.editor = (UserEditor) editor;
-               this.userAdminWrapper = userAdminWrapper;
-       }
-
-       protected void createFormContent(final IManagedForm mf) {
-               ScrolledForm form = mf.getForm();
-               Composite body = form.getBody();
-               GridLayout mainLayout = new GridLayout();
-               // mainLayout.marginRight = 10;
-               body.setLayout(mainLayout);
-               User user = editor.getDisplayedUser();
-               appendOverviewPart(body, user);
-               // Remove to ability to force the password for his own user. The user
-               // must then use the change pwd feature
-               if (!UserAdminUtils.isCurrentUser(user))
-                       appendPasswordPart(body, user);
-               appendMemberOfPart(body, user);
-       }
-
-       /** Creates the general section */
-       private void appendOverviewPart(final Composite parent, final User user) {
-               FormToolkit tk = getManagedForm().getToolkit();
-
-               Section section = addSection(tk, parent, "Main information");
-               Composite body = (Composite) section.getClient();
-               body.setLayout(new GridLayout(2, false));
-
-               final Text distinguishedName = createLT(tk, body, "User Name",
-                               UserAdminUtils.getProperty(user, LdifName.uid.name()));
-               distinguishedName.setEnabled(false);
-
-               final Text commonName = createLT(tk, body, "Common Name",
-                               UserAdminUtils.getProperty(user, LdifName.cn.name()));
-               commonName.setEnabled(false);
-
-               final Text firstName = createLT(tk, body, "First name",
-                               UserAdminUtils.getProperty(user, LdifName.givenName.name()));
-
-               final Text lastName = createLT(tk, body, "Last name",
-                               UserAdminUtils.getProperty(user, LdifName.sn.name()));
-
-               final Text email = createLT(tk, body, "Email",
-                               UserAdminUtils.getProperty(user, LdifName.mail.name()));
-
-               // create form part (controller)
-               AbstractFormPart part = new SectionPart((Section) body.getParent()) {
-                       private MainInfoListener listener;
-
-                       @Override
-                       public void initialize(IManagedForm form) {
-                               super.initialize(form);
-                               listener = editor.new MainInfoListener(parent.getDisplay(),
-                                               this);
-                               userAdminWrapper.addListener(listener);
-                       }
-
-                       @Override
-                       public void dispose() {
-                               userAdminWrapper.removeListener(listener);
-                               super.dispose();
-                       }
-
-                       @SuppressWarnings("unchecked")
-                       public void commit(boolean onSave) {
-                               // TODO Sanity checks (mail validity...)
-                               user.getProperties().put(LdifName.givenName.name(),
-                                               firstName.getText());
-                               user.getProperties()
-                                               .put(LdifName.sn.name(), lastName.getText());
-                               user.getProperties().put(LdifName.cn.name(),
-                                               commonName.getText());
-                               user.getProperties().put(LdifName.mail.name(), email.getText());
-                               super.commit(onSave);
-                       }
-
-                       @Override
-                       public void refresh() {
-                               distinguishedName.setText(UserAdminUtils.getProperty(user,
-                                               LdifName.uid.name()));
-                               commonName.setText(UserAdminUtils.getProperty(user,
-                                               LdifName.cn.name()));
-                               firstName.setText(UserAdminUtils.getProperty(user,
-                                               LdifName.givenName.name()));
-                               lastName.setText(UserAdminUtils.getProperty(user,
-                                               LdifName.sn.name()));
-                               email.setText(UserAdminUtils.getProperty(user,
-                                               LdifName.mail.name()));
-                               refreshFormTitle(user);
-                               super.refresh();
-                       }
-               };
-
-               // Improve this: automatically generate CN when first or last name
-               // changes
-               ModifyListener cnML = new ModifyListener() {
-                       private static final long serialVersionUID = 4298649222869835486L;
-
-                       @Override
-                       public void modifyText(ModifyEvent event) {
-                               String first = firstName.getText();
-                               String last = lastName.getText();
-                               String cn = first.trim() + " " + last.trim() + " ";
-                               cn = cn.trim();
-                               commonName.setText(cn);
-                               getManagedForm().getForm().setText(cn);
-                               editor.updateEditorTitle(cn);
-                       }
-               };
-               firstName.addModifyListener(cnML);
-               lastName.addModifyListener(cnML);
-
-               ModifyListener defaultListener = editor.new FormPartML(part);
-               firstName.addModifyListener(defaultListener);
-               lastName.addModifyListener(defaultListener);
-               email.addModifyListener(defaultListener);
-               getManagedForm().addPart(part);
-       }
-
-       /** Creates the password section */
-       private void appendPasswordPart(Composite parent, final User user) {
-               FormToolkit tk = getManagedForm().getToolkit();
-               Section section = addSection(tk, parent, "Password");
-               Composite body = (Composite) section.getClient();
-               body.setLayout(new GridLayout(2, false));
-
-               // add widgets (view)
-               final Text password1 = createLP(tk, body, "New password", "");
-               final Text password2 = createLP(tk, body, "Repeat password", "");
-
-               // create form part (controller)
-               AbstractFormPart part = new SectionPart((Section) body.getParent()) {
-                       @SuppressWarnings("unchecked")
-                       public void commit(boolean onSave) {
-                               if (!password1.getText().equals("")
-                                               || !password2.getText().equals("")) {
-                                       if (password1.getText().equals(password2.getText())) {
-                                               char[] newPassword = password1.getText().toCharArray();
-                                               // userAdminWrapper.beginTransactionIfNeeded();
-                                               user.getCredentials().put(null, newPassword);
-                                               password1.setText("");
-                                               password2.setText("");
-                                               super.commit(onSave);
-                                       } else {
-                                               password1.setText("");
-                                               password2.setText("");
-                                               throw new CmsException("Passwords are not equals");
-                                       }
-                               }
-                       }
-               };
-               ModifyListener defaultListener = editor.new FormPartML(part);
-               password1.addModifyListener(defaultListener);
-               password2.addModifyListener(defaultListener);
-               getManagedForm().addPart(part);
-       }
-
-       private LdifUsersTable appendMemberOfPart(final Composite parent, User user) {
-               FormToolkit tk = getManagedForm().getToolkit();
-               Section section = addSection(tk, parent, "Roles");
-               Composite body = (Composite) section.getClient();
-               body.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-               boolean isAdmin = UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN);
-
-               // Displayed columns
-               List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-               columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24));
-               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name",
-                               150));
-               columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain Name",
-                               200));
-               // Only show technical DN to administrators
-               if (isAdmin)
-                       columnDefs.add(new ColumnDefinition(new UserNameLP(),
-                                       "Distinguished Name", 120));
-
-               // Create and configure the table
-               final LdifUsersTable userViewerCmp = new MyUserTableViewer(body,
-                               SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, user);
-
-               userViewerCmp.setColumnDefinitions(columnDefs);
-               if (isAdmin)
-                       userViewerCmp.populateWithStaticFilters(false, false);
-               else
-                       userViewerCmp.populate(true, false);
-               GridData gd = EclipseUiUtils.fillAll();
-               gd.heightHint = 300;
-               userViewerCmp.setLayoutData(gd);
-
-               // Controllers
-               TableViewer userViewer = userViewerCmp.getTableViewer();
-               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener());
-               int operations = DND.DROP_COPY | DND.DROP_MOVE;
-               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
-               GroupDropListener dropL = new GroupDropListener(userAdminWrapper,
-                               userViewer, user);
-               userViewer.addDropSupport(operations, tt, dropL);
-
-               SectionPart part = new SectionPart((Section) body.getParent()) {
-
-                       private GroupChangeListener listener;
-
-                       @Override
-                       public void initialize(IManagedForm form) {
-                               super.initialize(form);
-                               listener = editor.new GroupChangeListener(parent.getDisplay(),
-                                               this);
-                               userAdminWrapper.addListener(listener);
-                       }
-
-                       public void commit(boolean onSave) {
-                               super.commit(onSave);
-                       }
-
-                       @Override
-                       public void dispose() {
-                               userAdminWrapper.removeListener(listener);
-                               super.dispose();
-                       }
-
-                       @Override
-                       public void refresh() {
-                               userViewerCmp.refresh();
-                               super.refresh();
-                       }
-               };
-               getManagedForm().addPart(part);
-               addRemoveAbitily(part, userViewer, user);
-               return userViewerCmp;
-       }
-
-       private class MyUserTableViewer extends LdifUsersTable {
-               private static final long serialVersionUID = 2653790051461237329L;
-
-               private Button showSystemRoleBtn;
-
-               private final User user;
-               private final UserFilter userFilter;
-
-               public MyUserTableViewer(Composite parent, int style, User user) {
-                       super(parent, style, true);
-                       this.user = user;
-                       userFilter = new UserFilter();
-                       userFilter.setShowSystemRole(false);
-               }
-
-               protected void populateStaticFilters(Composite staticFilterCmp) {
-                       staticFilterCmp.setLayout(new GridLayout());
-                       showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK);
-                       showSystemRoleBtn.setText("Show system roles");
-                       showSystemRoleBtn.addSelectionListener(new SelectionAdapter() {
-                               private static final long serialVersionUID = -7033424592697691676L;
-
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       userFilter.setShowSystemRole(showSystemRoleBtn
-                                                       .getSelection());
-                                       refresh();
-                               }
-                       });
-               }
-
-               @Override
-               protected List<User> listFilteredElements(String filter) {
-                       List<User> users = (List<User>) editor.getFlatGroups(null);
-                       List<User> filteredUsers = new ArrayList<User>();
-                       if (users.contains(user))
-                               users.remove(user);
-                       userFilter.setSearchText(filter);
-                       for (User user : users)
-                               if (userFilter.select(null, null, user))
-                                       filteredUsers.add(user);
-                       return filteredUsers;
-               }
-       }
-
-       private void addRemoveAbitily(SectionPart sectionPart,
-                       TableViewer userViewer, User user) {
-               Section section = sectionPart.getSection();
-               ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
-               ToolBar toolbar = toolBarManager.createControl(section);
-               final Cursor handCursor = new Cursor(section.getDisplay(),
-                               SWT.CURSOR_HAND);
-               toolbar.setCursor(handCursor);
-               toolbar.addDisposeListener(new DisposeListener() {
-                       private static final long serialVersionUID = 3882131405820522925L;
-
-                       public void widgetDisposed(DisposeEvent e) {
-                               if ((handCursor != null) && (handCursor.isDisposed() == false)) {
-                                       handCursor.dispose();
-                               }
-                       }
-               });
-
-               String tooltip = "Remove " + UserAdminUtils.getUsername(user)
-                               + " from the below selected groups";
-               Action action = new RemoveMembershipAction(userViewer, user, tooltip,
-                               SecurityAdminImages.ICON_REMOVE_DESC);
-               toolBarManager.add(action);
-               toolBarManager.update(true);
-               section.setTextClient(toolbar);
-       }
-
-       private class RemoveMembershipAction extends Action {
-               private static final long serialVersionUID = -1337713097184522588L;
-
-               private final TableViewer userViewer;
-               private final User user;
-
-               RemoveMembershipAction(TableViewer userViewer, User user, String name,
-                               ImageDescriptor img) {
-                       super(name, img);
-                       this.userViewer = userViewer;
-                       this.user = user;
-               }
-
-               @Override
-               public void run() {
-                       ISelection selection = userViewer.getSelection();
-                       if (selection.isEmpty())
-                               return;
-
-                       @SuppressWarnings("unchecked")
-                       Iterator<Group> it = ((IStructuredSelection) selection).iterator();
-                       List<Group> groups = new ArrayList<Group>();
-                       while (it.hasNext()) {
-                               Group currGroup = it.next();
-                               groups.add(currGroup);
-                       }
-
-                       userAdminWrapper.beginTransactionIfNeeded();
-                       for (Group group : groups) {
-                               group.removeMember(user);
-                       }
-                       userAdminWrapper.commitOrNotifyTransactionStateChange();
-                       for (Group group : groups) {
-                               userAdminWrapper.notifyListeners(new UserAdminEvent(null,
-                                               UserAdminEvent.ROLE_CHANGED, group));
-                       }
-               }
-       }
-
-       /**
-        * Defines the table as being a potential target to add group memberships
-        * (roles) to this user
-        */
-       private class GroupDropListener extends ViewerDropAdapter {
-               private static final long serialVersionUID = 2893468717831451621L;
-
-               private final UserAdminWrapper myUserAdminWrapper;
-               private final User myUser;
-
-               public GroupDropListener(UserAdminWrapper userAdminWrapper,
-                               Viewer userViewer, User user) {
-                       super(userViewer);
-                       this.myUserAdminWrapper = userAdminWrapper;
-                       this.myUser = user;
-               }
-
-               @Override
-               public boolean validateDrop(Object target, int operation,
-                               TransferData transferType) {
-                       // Target is always OK in a list only view
-                       // TODO check if not a string
-                       boolean validDrop = true;
-                       return validDrop;
-               }
-
-               @Override
-               public void drop(DropTargetEvent event) {
-                       String name = (String) event.data;
-                       UserAdmin myUserAdmin = myUserAdminWrapper.getUserAdmin();
-                       Role role = myUserAdmin.getRole(name);
-                       // TODO this check should be done before.
-                       if (role.getType() == Role.GROUP) {
-                               // TODO check if the user is already member of this group
-
-                               myUserAdminWrapper.beginTransactionIfNeeded();
-                               Group group = (Group) role;
-                               group.addMember(myUser);
-                               userAdminWrapper.commitOrNotifyTransactionStateChange();
-                               myUserAdminWrapper.notifyListeners(new UserAdminEvent(null,
-                                               UserAdminEvent.ROLE_CHANGED, group));
-                       }
-                       super.drop(event);
-               }
-
-               @Override
-               public boolean performDrop(Object data) {
-                       // userTableViewerCmp.refresh();
-                       return true;
-               }
-       }
-
-       // LOCAL HELPERS
-       private void refreshFormTitle(User group) {
-               getManagedForm().getForm().setText(
-                               UserAdminUtils.getProperty(group, LdifName.cn.name()));
-       }
-
-       /** Appends a section with a title */
-       private Section addSection(FormToolkit tk, Composite parent, String title) {
-               Section section = tk.createSection(parent, Section.TITLE_BAR);
-               GridData gd = EclipseUiUtils.fillWidth();
-               gd.verticalAlignment = PRE_TITLE_INDENT;
-               section.setLayoutData(gd);
-               section.setText(title);
-               // section.getMenu().setVisible(true);
-
-               Composite body = tk.createComposite(section, SWT.WRAP);
-               body.setLayoutData(EclipseUiUtils.fillAll());
-               section.setClient(body);
-
-               return section;
-       }
-
-       /** Creates label and multiline text. */
-       Text createLMT(FormToolkit toolkit, Composite body, String label,
-                       String value) {
-               Label lbl = toolkit.createLabel(body, label);
-               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
-               Text text = toolkit.createText(body, value, SWT.BORDER | SWT.MULTI);
-               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
-               return text;
-       }
-
-       /** Creates label and password. */
-       Text createLP(FormToolkit toolkit, Composite body, String label,
-                       String value) {
-               Label lbl = toolkit.createLabel(body, label);
-               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
-               Text text = toolkit.createText(body, value, SWT.BORDER | SWT.PASSWORD);
-               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-               return text;
-       }
-
-       /** Creates label and text. */
-       Text createLT(FormToolkit toolkit, Composite body, String label,
-                       String value) {
-               Label lbl = toolkit.createLabel(body, label);
-               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
-               Text text = toolkit.createText(body, value, SWT.BORDER);
-               text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-               return text;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UsersView.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/parts/UsersView.java
deleted file mode 100644 (file)
index a397432..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.admin.internal.parts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.parts.LdifUsersTable;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.osgi.useradmin.LdifName;
-import org.argeo.security.ui.admin.SecurityAdminPlugin;
-import org.argeo.security.ui.admin.internal.UiUserAdminListener;
-import org.argeo.security.ui.admin.internal.UserAdminWrapper;
-import org.argeo.security.ui.admin.internal.providers.CommonNameLP;
-import org.argeo.security.ui.admin.internal.providers.DomainNameLP;
-import org.argeo.security.ui.admin.internal.providers.MailLP;
-import org.argeo.security.ui.admin.internal.providers.UserDragListener;
-import org.argeo.security.ui.admin.internal.providers.UserNameLP;
-import org.argeo.security.ui.admin.internal.providers.UserTableDefaultDClickListener;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.part.ViewPart;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdminEvent;
-import org.osgi.service.useradmin.UserAdminListener;
-
-/** List all users with filter - based on Ldif userAdmin */
-public class UsersView extends ViewPart implements ArgeoNames {
-       // private final static Log log = LogFactory.getLog(UsersView.class);
-
-       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
-                       + ".usersView";
-
-       /* DEPENDENCY INJECTION */
-       private UserAdminWrapper userAdminWrapper;
-
-       // UI Objects
-       private LdifUsersTable userTableViewerCmp;
-       private TableViewer userViewer;
-       private List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-
-       private UserAdminListener listener;
-
-       @Override
-       public void createPartControl(Composite parent) {
-
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-               // Define the displayed columns
-               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name",
-                               150));
-               columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
-               columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
-               // Only show technical DN to admin
-               if (UserAdminUtils.isUserInRole(AuthConstants.ROLE_ADMIN))
-                       columnDefs.add(new ColumnDefinition(new UserNameLP(),
-                                       "Distinguished Name", 300));
-
-               // Create and configure the table
-               userTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI
-                               | SWT.H_SCROLL | SWT.V_SCROLL);
-               userTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll());
-               userTableViewerCmp.setColumnDefinitions(columnDefs);
-               userTableViewerCmp.populate(true, false);
-
-               // Links
-               userViewer = userTableViewerCmp.getTableViewer();
-               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener());
-               getViewSite().setSelectionProvider(userViewer);
-
-               // Really?
-               userTableViewerCmp.refresh();
-
-               // Drag and drop
-               int operations = DND.DROP_COPY | DND.DROP_MOVE;
-               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
-               userViewer.addDragSupport(operations, tt, new UserDragListener(
-                               userViewer));
-
-               // Register a useradmin listener
-               listener = new MyUiUAListener(parent.getDisplay());
-               userAdminWrapper.addListener(listener);
-       }
-
-       private class MyUiUAListener extends UiUserAdminListener {
-               public MyUiUAListener(Display display) {
-                       super(display);
-               }
-
-               @Override
-               public void roleChangedToUiThread(UserAdminEvent event) {
-                       if (userViewer != null && !userViewer.getTable().isDisposed())
-                               refresh();
-               }
-       }
-
-       private class MyUserTableViewer extends LdifUsersTable {
-               private static final long serialVersionUID = 8467999509931900367L;
-
-               private final String[] knownProps = { LdifName.uid.name(),
-                               LdifName.dn.name(), LdifName.cn.name(),
-                               LdifName.givenName.name(), LdifName.sn.name(),
-                               LdifName.mail.name() };
-
-               public MyUserTableViewer(Composite parent, int style) {
-                       super(parent, style);
-               }
-
-               @Override
-               protected List<User> listFilteredElements(String filter) {
-                       Role[] roles;
-
-                       try {
-                               StringBuilder builder = new StringBuilder();
-
-                               StringBuilder tmpBuilder = new StringBuilder();
-                               if (EclipseUiUtils.notEmpty(filter))
-                                       for (String prop : knownProps) {
-                                               tmpBuilder.append("(");
-                                               tmpBuilder.append(prop);
-                                               tmpBuilder.append("=*");
-                                               tmpBuilder.append(filter);
-                                               tmpBuilder.append("*)");
-                                       }
-                               if (tmpBuilder.length() > 1) {
-                                       builder.append("(&(").append(LdifName.objectClass.name())
-                                                       .append("=").append(LdifName.inetOrgPerson.name())
-                                                       .append(")(|");
-                                       builder.append(tmpBuilder.toString());
-                                       builder.append("))");
-                               } else
-                                       builder.append("(").append(LdifName.objectClass.name())
-                                                       .append("=").append(LdifName.inetOrgPerson.name())
-                                                       .append(")");
-                               roles = userAdminWrapper.getUserAdmin().getRoles(
-                                               builder.toString());
-                       } catch (InvalidSyntaxException e) {
-                               throw new CmsException("Unable to get roles with filter: "
-                                               + filter, e);
-                       }
-                       List<User> users = new ArrayList<User>();
-                       for (Role role : roles)
-                               // if (role.getType() == Role.USER && role.getType() !=
-                               // Role.GROUP)
-                               users.add((User) role);
-                       return users;
-               }
-       }
-
-       public void refresh() {
-               userTableViewerCmp.refresh();
-       }
-
-       // Override generic view methods
-       @Override
-       public void dispose() {
-               userAdminWrapper.removeListener(listener);
-               super.dispose();
-       }
-
-       @Override
-       public void setFocus() {
-               userTableViewerCmp.setFocus();
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/CommonNameLP.java
deleted file mode 100644 (file)
index d45c0b6..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.argeo.security.ui.admin.internal.providers;
-
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.argeo.osgi.useradmin.LdifName;
-import org.osgi.service.useradmin.User;
-
-/** Simply declare a label provider that returns the common name of a user */
-public class CommonNameLP extends UserAdminAbstractLP {
-       private static final long serialVersionUID = 5256703081044911941L;
-
-       @Override
-       public String getText(User user) {
-               return UserAdminUtils.getProperty(user, LdifName.cn.name());
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/DomainNameLP.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/DomainNameLP.java
deleted file mode 100644 (file)
index 795fd0a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.argeo.security.ui.admin.internal.providers;
-
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.osgi.service.useradmin.User;
-
-/** The human friendly domain name for the corresponding user. */
-public class DomainNameLP extends UserAdminAbstractLP {
-       private static final long serialVersionUID = 5256703081044911941L;
-
-       @Override
-       public String getText(User user) {
-               return UserAdminUtils.getDomainName(user);
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/MailLP.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/MailLP.java
deleted file mode 100644 (file)
index 0a6dcb6..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.argeo.security.ui.admin.internal.providers;
-
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.argeo.osgi.useradmin.LdifName;
-import org.osgi.service.useradmin.User;
-
-/** Simply declare a label provider that returns the Primary Mail of a user */
-public class MailLP extends UserAdminAbstractLP {
-       private static final long serialVersionUID = 8329764452141982707L;
-
-       @Override
-       public String getText(User user) {
-               return UserAdminUtils.getProperty(user, LdifName.mail.name());
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/RoleIconLP.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/RoleIconLP.java
deleted file mode 100644 (file)
index bb19220..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.argeo.security.ui.admin.internal.providers;
-
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.security.ui.admin.SecurityAdminImages;
-import org.eclipse.swt.graphics.Image;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-
-/** Provide a bundle specific image depending on the current user type */
-public class RoleIconLP extends UserAdminAbstractLP {
-       private static final long serialVersionUID = 6550449442061090388L;
-
-       @Override
-       public String getText(User user) {
-               return "";
-       }
-
-       @Override
-       public Image getImage(Object element) {
-               User user = (User) element;
-               String dn = user.getName();
-               if (dn.endsWith(AuthConstants.ROLES_BASEDN))
-                       return SecurityAdminImages.ICON_ROLE;
-               else if (user.getType() == Role.GROUP)
-                       return SecurityAdminImages.ICON_GROUP;
-               else
-                       return SecurityAdminImages.ICON_USER;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserAdminAbstractLP.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserAdminAbstractLP.java
deleted file mode 100644 (file)
index 2b0a13d..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.argeo.security.ui.admin.internal.providers;
-
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.eclipse.jface.resource.JFaceResources;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Font;
-import org.eclipse.swt.widgets.Display;
-import org.osgi.service.useradmin.User;
-
-/**
- * Utility class that add font modifications to a column label provider
- * depending on the given user properties
- */
-public abstract class UserAdminAbstractLP extends ColumnLabelProvider {
-       private static final long serialVersionUID = 137336765024922368L;
-
-       // private Font italic;
-       private Font bold;
-
-       @Override
-       public Font getFont(Object element) {
-               // Self as bold
-               try {
-                       LdapName selfUserName = UserAdminUtils.getCurrentUserLdapName();
-                       String userName = ((User) element).getName();
-                       LdapName userLdapName = new LdapName(userName);
-                       if (userLdapName.equals(selfUserName)) {
-                               if (bold == null)
-                                       bold = JFaceResources.getFontRegistry()
-                                                       .defaultFontDescriptor().setStyle(SWT.BOLD)
-                                                       .createFont(Display.getCurrent());
-                               return bold;
-                       }
-               } catch (InvalidNameException e) {
-                       throw new CmsException("cannot parse dn for " + element, e);
-               }
-
-               // Disabled as Italic
-               // Node userProfile = (Node) elem;
-               // if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean())
-               // return italic;
-
-               return null;
-               // return super.getFont(element);
-       }
-
-       @Override
-       public String getText(Object element) {
-               User user = (User) element;
-               return getText(user);
-       }
-
-       public void setDisplay(Display display) {
-               // italic = JFaceResources.getFontRegistry().defaultFontDescriptor()
-               // .setStyle(SWT.ITALIC).createFont(display);
-               bold = JFaceResources.getFontRegistry().defaultFontDescriptor()
-                               .setStyle(SWT.BOLD).createFont(Display.getCurrent());
-       }
-
-       public abstract String getText(User user);
-}
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserDragListener.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserDragListener.java
deleted file mode 100644 (file)
index f60b64c..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.argeo.security.ui.admin.internal.providers;
-
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.dnd.DragSourceEvent;
-import org.eclipse.swt.dnd.DragSourceListener;
-import org.osgi.service.useradmin.User;
-
-/** Default drag listener to modify group and users via the UI */
-public class UserDragListener implements DragSourceListener {
-       private static final long serialVersionUID = -2074337775033781454L;
-       private final Viewer viewer;
-
-       public UserDragListener(Viewer viewer) {
-               this.viewer = viewer;
-       }
-
-       public void dragStart(DragSourceEvent event) {
-               // TODO implement finer checks
-               IStructuredSelection selection = (IStructuredSelection) viewer
-                               .getSelection();
-               if (selection.isEmpty() || selection.size() > 1)
-                       event.doit = false;
-               else
-                       event.doit = true;
-       }
-
-       public void dragSetData(DragSourceEvent event) {
-               // TODO Support multiple selection
-               Object obj = ((IStructuredSelection) viewer.getSelection())
-                               .getFirstElement();
-               if (obj != null) {
-                       User user = (User) obj;
-                       event.data = user.getName();
-               }
-       }
-
-       public void dragFinished(DragSourceEvent event) {
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserFilter.java
deleted file mode 100644 (file)
index 5f753d1..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-package org.argeo.security.ui.admin.internal.providers;
-
-import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty;
-
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.cms.util.useradmin.UserAdminUtils;
-import org.argeo.osgi.useradmin.LdifName;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.jface.viewers.ViewerFilter;
-import org.osgi.service.useradmin.User;
-
-public class UserFilter extends ViewerFilter {
-       private static final long serialVersionUID = 5082509381672880568L;
-
-       private String searchString;
-       private boolean showSystemRole = true;
-
-       private final String[] knownProps = { LdifName.dn.name(),
-                       LdifName.cn.name(), LdifName.givenName.name(), LdifName.sn.name(),
-                       LdifName.uid.name(), LdifName.description.name(),
-                       LdifName.mail.name() };
-
-       public void setSearchText(String s) {
-               // ensure that the value can be used for matching
-               if (notEmpty(s))
-                       searchString = ".*" + s.toLowerCase() + ".*";
-               else
-                       searchString = ".*";
-       }
-
-       public void setShowSystemRole(boolean showSystemRole) {
-               this.showSystemRole = showSystemRole;
-       }
-
-       @Override
-       public boolean select(Viewer viewer, Object parentElement, Object element) {
-               User user = (User) element;
-               if (!showSystemRole
-                               && user.getName().matches(
-                                               ".*(" + AuthConstants.ROLES_BASEDN + ")"))
-                       // UserAdminUtils.getProperty(user, LdifName.dn.name())
-                       // .toLowerCase().endsWith(AuthConstants.ROLES_BASEDN))
-                       return false;
-
-               if (searchString == null || searchString.length() == 0)
-                       return true;
-
-               if (user.getName().matches(searchString))
-                       return true;
-
-               for (String key : knownProps) {
-                       String currVal = UserAdminUtils.getProperty(user, key);
-                       if (notEmpty(currVal)
-                                       && currVal.toLowerCase().matches(searchString))
-                               return true;
-               }
-               return false;
-       }
-
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserNameLP.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserNameLP.java
deleted file mode 100644 (file)
index a2a15c1..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.argeo.security.ui.admin.internal.providers;
-
-import org.osgi.service.useradmin.User;
-
-/** Simply declare a label provider that returns the username of a user */
-public class UserNameLP extends UserAdminAbstractLP {
-       private static final long serialVersionUID = 6550449442061090388L;
-
-       @Override
-       public String getText(User user) {
-               return user.getName();
-       }
-}
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserTableDefaultDClickListener.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserTableDefaultDClickListener.java
deleted file mode 100644 (file)
index 8f4a35a..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.argeo.security.ui.admin.internal.providers;
-
-import org.argeo.cms.CmsException;
-import org.argeo.eclipse.ui.workbench.WorkbenchUiPlugin;
-import org.argeo.security.ui.admin.internal.parts.UserEditor;
-import org.argeo.security.ui.admin.internal.parts.UserEditorInput;
-import org.eclipse.jface.viewers.DoubleClickEvent;
-import org.eclipse.jface.viewers.IDoubleClickListener;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.PartInitException;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.User;
-
-/**
- * Default double click listener for the various user tables, will open the
- * clicked item in the editor
- */
-public class UserTableDefaultDClickListener implements IDoubleClickListener {
-       public void doubleClick(DoubleClickEvent evt) {
-               if (evt.getSelection().isEmpty())
-                       return;
-               Object obj = ((IStructuredSelection) evt.getSelection())
-                               .getFirstElement();
-               User user = (User) obj;
-               IWorkbenchWindow iww = WorkbenchUiPlugin.getDefault().getWorkbench()
-                               .getActiveWorkbenchWindow();
-               IWorkbenchPage iwp = iww.getActivePage();
-               UserEditorInput uei = new UserEditorInput(user.getName());
-
-               try {
-                       // Works around the fact that dynamic setting of the editor icon
-                       // causes NPE after a login/logout on RAP
-                       if (user instanceof Group)
-                               iwp.openEditor(uei, UserEditor.GROUP_EDITOR_ID);
-                       else
-                               iwp.openEditor(uei, UserEditor.USER_EDITOR_ID);
-               } catch (PartInitException pie) {
-                       throw new CmsException("Unable to open UserEditor for " + user,
-                                       pie);
-               }
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserTransactionProvider.java b/org.argeo.security.ui.admin/src/org/argeo/security/ui/admin/internal/providers/UserTransactionProvider.java
deleted file mode 100644 (file)
index 4ba304b..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-package org.argeo.security.ui.admin.internal.providers;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.transaction.Status;
-import javax.transaction.UserTransaction;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.argeo.security.ui.admin.SecurityAdminPlugin;
-import org.eclipse.ui.AbstractSourceProvider;
-import org.eclipse.ui.ISources;
-
-/** Observe and notify UI on UserTransaction state changes */
-public class UserTransactionProvider extends AbstractSourceProvider {
-       private final static Log log = LogFactory
-                       .getLog(UserTransactionProvider.class);
-
-       public final static String TRANSACTION_STATE = SecurityAdminPlugin.PLUGIN_ID
-                       + ".userTransactionState";
-       public final static String STATUS_ACTIVE = "status.active";
-       public final static String STATUS_NO_TRANSACTION = "status.noTransaction";
-
-       /* DEPENDENCY INJECTION */
-       private UserTransaction userTransaction;
-
-       @Override
-       public String[] getProvidedSourceNames() {
-               return new String[] { TRANSACTION_STATE};
-       }
-
-       @Override
-       public Map<String, String> getCurrentState() {
-               Map<String, String> currentState = new HashMap<String, String>(1);
-                       currentState.put(TRANSACTION_STATE, getInternalCurrentState());
-               return currentState;
-       }
-
-       @Override
-       public void dispose() {
-       }
-
-       private String getInternalCurrentState() {
-               try {
-                       String transactionState;
-                       if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION)
-                               transactionState = STATUS_NO_TRANSACTION;
-                       else
-                               // if (userTransaction.getStatus() == Status.STATUS_ACTIVE)
-                               transactionState = STATUS_ACTIVE;
-                       return transactionState;
-               } catch (Exception e) {
-                       throw new CmsException("Unable to begin transaction", e);
-               }
-       }
-
-       /** Publishes the ability to notify a state change */
-       public void fireTransactionStateChange() {
-               try {
-                       fireSourceChanged(ISources.WORKBENCH, TRANSACTION_STATE,
-                                       getInternalCurrentState());
-               } catch (Exception e) {
-                       log.warn("Cannot fire transaction state change event. Caught exception: "
-                                       + e.getClass().getCanonicalName() + " - " + e.getMessage());
-               }
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserTransaction(UserTransaction userTransaction) {
-               this.userTransaction = userTransaction;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.ui.rap/.classpath b/org.argeo.security.ui.rap/.classpath
deleted file mode 100644 (file)
index 457b115..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="src" path="src" />
-       <classpathentry kind="con"
-               path="org.eclipse.pde.core.requiredPlugins" />
-       <classpathentry kind="con"
-               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" />
-       <classpathentry kind="output" path="bin" />
-</classpath>
diff --git a/org.argeo.security.ui.rap/.project b/org.argeo.security.ui.rap/.project
deleted file mode 100644 (file)
index d20bec7..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.security.ui.rap</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.pde.PluginNature</nature>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-       </natures>
-</projectDescription>
diff --git a/org.argeo.security.ui.rap/META-INF/spring/commands.xml b/org.argeo.security.ui.rap/META-INF/spring/commands.xml
deleted file mode 100644 (file)
index 802d31d..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans
-        http://www.springframework.org/schema/beans/spring-beans.xsd">
-
-       <bean id="openChangePasswordDialog" class="org.argeo.security.ui.commands.OpenChangePasswordDialog"
-               scope="prototype">
-               <property name="userAdmin" ref="userAdmin" />
-               <property name="userTransaction" ref="userTransaction" />
-       </bean>
-
-
-       <!-- RAP Specific command and corresponding service to enable open file -->
-       <bean id="org.argeo.eclipse.ui.workbench.openFile" class="org.argeo.eclipse.ui.workbench.commands.OpenFile"
-       scope="prototype">
-               <property name="openFileServiceId"
-                       value="org.argeo.security.ui.specific.openFileService" />
-       </bean>
-       <!-- Useless - nothing to inject -->
-       <!-- <bean id="org.argeo.security.ui.specific.openFileService" class="org.argeo.eclipse.ui.specific.OpenFileService" 
-               scope="prototype"> </bean> -->
-</beans>
diff --git a/org.argeo.security.ui.rap/META-INF/spring/osgi.xml b/org.argeo.security.ui.rap/META-INF/spring/osgi.xml
deleted file mode 100644 (file)
index 84e5d7b..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>\r
-<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
-       xmlns:osgi="http://www.springframework.org/schema/osgi"\r
-       xsi:schemaLocation="http://www.springframework.org/schema/osgi  \r
-       http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
-       http://www.springframework.org/schema/beans   \r
-       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"\r
-       osgi:default-timeout="30000">\r
-\r
-       <reference id="userAdmin" interface="org.osgi.service.useradmin.UserAdmin" />\r
-       <reference id="userTransaction" interface="javax.transaction.UserTransaction" />\r
-</beans:beans>
\ No newline at end of file
diff --git a/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle.properties b/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle.properties
deleted file mode 100644 (file)
index 4dff7af..0000000
+++ /dev/null
@@ -1 +0,0 @@
-changePassword=Change password
\ No newline at end of file
diff --git a/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_fr.properties b/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_fr.properties
deleted file mode 100644 (file)
index 158d6fa..0000000
+++ /dev/null
@@ -1 +0,0 @@
-changePassword=Changer de mot de passe
\ No newline at end of file
diff --git a/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_ru.properties b/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_ru.properties
deleted file mode 100644 (file)
index 11dd100..0000000
+++ /dev/null
@@ -1 +0,0 @@
-changePassword=\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C
diff --git a/org.argeo.security.ui.rap/bnd.bnd b/org.argeo.security.ui.rap/bnd.bnd
deleted file mode 100644 (file)
index 1da6c05..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-Bundle-SymbolicName: org.argeo.security.ui.rap;singleton:=true
-Bundle-Activator: org.argeo.security.ui.rap.SecureRapActivator
-Bundle-ActivationPolicy: lazy
-Require-Bundle: org.eclipse.rap.ui,org.eclipse.core.runtime
-
-Import-Package: org.argeo.eclipse.spring,\
-org.argeo.eclipse.ui.specific,\
-org.argeo.eclipse.ui.workbench.commands,\
-org.argeo.cms,\
-org.argeo.cms.auth,\
-org.argeo.security.ui,\
-*
diff --git a/org.argeo.security.ui.rap/branding/afterLogout.html b/org.argeo.security.ui.rap/branding/afterLogout.html
deleted file mode 100644 (file)
index ae0901b..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<html>
-<head></head>
-<body>
-<center>
-<table height="100%">
-<tr>
-       <td style="vertical-align:middle">
-               <a 
-                       style="font-family:sans-serif;color:#0066CC;text-decoration:none;" 
-                       href="node" 
-                       title="Click to log in"
-               >Login...</a>
-       </td>
-</tr>
-</table>
-</center>
-</body>
-</html>
\ No newline at end of file
diff --git a/org.argeo.security.ui.rap/branding/empty.html b/org.argeo.security.ui.rap/branding/empty.html
deleted file mode 100644 (file)
index 94fe28a..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<head></head>
-<body>
-</body>
-</html>
\ No newline at end of file
diff --git a/org.argeo.security.ui.rap/branding/favicon.ico b/org.argeo.security.ui.rap/branding/favicon.ico
deleted file mode 100644 (file)
index 213cdf7..0000000
Binary files a/org.argeo.security.ui.rap/branding/favicon.ico and /dev/null differ
diff --git a/org.argeo.security.ui.rap/branding/login.html b/org.argeo.security.ui.rap/branding/login.html
deleted file mode 100644 (file)
index 6de7eb2..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<html>
-<head></head>
-<body>
-<center>
-<table height="100%">
-<tr>
-       <td style="vertical-align:middle">
-               <a 
-                       style="font-family:sans-serif;color:#0066CC;text-decoration:none;" 
-                       href="javascript:location.reload(true);" 
-                       title="Click to log in"
-               >Login...</a>
-       </td>
-</tr>
-</table>
-</center>
-</body>
-</html>
\ No newline at end of file
diff --git a/org.argeo.security.ui.rap/branding/public.html b/org.argeo.security.ui.rap/branding/public.html
deleted file mode 100644 (file)
index e50f6e9..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<html>
-<head></head>
-<body>
-<center>
-<table height="100%">
-<tr>
-       <td style="vertical-align:middle">
-               <a 
-                       style="font-family:sans-serif;color:#0066CC;text-decoration:none;" 
-                       href="javascript:location.reload(true);" 
-                       title="Refresh"
-               >Refresh...</a>
-       </td>
-</tr>
-</table>
-</center>
-</body>
-</html>
\ No newline at end of file
diff --git a/org.argeo.security.ui.rap/build.properties b/org.argeo.security.ui.rap/build.properties
deleted file mode 100644 (file)
index ae37429..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-source.. = src/
-bin.includes = OSGI-INF/
diff --git a/org.argeo.security.ui.rap/icons/closeAll.gif b/org.argeo.security.ui.rap/icons/closeAll.gif
deleted file mode 100644 (file)
index 28a3785..0000000
Binary files a/org.argeo.security.ui.rap/icons/closeAll.gif and /dev/null differ
diff --git a/org.argeo.security.ui.rap/icons/exit.png b/org.argeo.security.ui.rap/icons/exit.png
deleted file mode 100644 (file)
index cfbf9d1..0000000
Binary files a/org.argeo.security.ui.rap/icons/exit.png and /dev/null differ
diff --git a/org.argeo.security.ui.rap/icons/home.gif b/org.argeo.security.ui.rap/icons/home.gif
deleted file mode 100644 (file)
index fd0c669..0000000
Binary files a/org.argeo.security.ui.rap/icons/home.gif and /dev/null differ
diff --git a/org.argeo.security.ui.rap/icons/main.gif b/org.argeo.security.ui.rap/icons/main.gif
deleted file mode 100644 (file)
index 90a0014..0000000
Binary files a/org.argeo.security.ui.rap/icons/main.gif and /dev/null differ
diff --git a/org.argeo.security.ui.rap/icons/password.gif b/org.argeo.security.ui.rap/icons/password.gif
deleted file mode 100644 (file)
index a6b251f..0000000
Binary files a/org.argeo.security.ui.rap/icons/password.gif and /dev/null differ
diff --git a/org.argeo.security.ui.rap/icons/preferences.png b/org.argeo.security.ui.rap/icons/preferences.png
deleted file mode 100644 (file)
index aa0dc0b..0000000
Binary files a/org.argeo.security.ui.rap/icons/preferences.png and /dev/null differ
diff --git a/org.argeo.security.ui.rap/plugin.xml b/org.argeo.security.ui.rap/plugin.xml
deleted file mode 100644 (file)
index 243a433..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?eclipse version="3.4"?>
-<plugin>
-   <extension
-         point="org.eclipse.rap.ui.entrypoint">
-      <entrypoint
-            id="org.argeo.security.ui.rap.secureEntryPoint"
-            class="org.argeo.security.ui.rap.RapWorkbenchLogin"
-            path="/node"
-            brandingId="org.argeo.security.ui.rap.defaultBranding">
-      </entrypoint>
-      <entrypoint
-            id="org.argeo.security.ui.rap.anonymousEntryPoint"
-            class="org.argeo.security.ui.rap.AnonymousEntryPoint"
-            path="/public"
-            brandingId="org.argeo.security.ui.rap.defaultBranding">
-      </entrypoint>
-      <entrypoint
-            brandingId="org.argeo.security.ui.rap.defaultBranding"
-            class="org.argeo.security.ui.rap.RapWorkbenchLogin"
-            id="org.argeo.security.ui.rap.secureEntryPoint"
-            path="/login">
-      </entrypoint>
-   </extension>
-
-       <!-- COMMANDS --> 
-       <extension point="org.eclipse.ui.commands">
-               <command
-                       id="org.argeo.security.ui.rap.mainMenuCommand"
-                       defaultHandler="org.argeo.security.ui.rap.commands.OpenHome"
-                       name="Main"> 
-               </command>
-               <command
-                       id="org.argeo.security.ui.rap.openChangePasswordDialog"
-                       defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
-                       name="%changePassword">
-               </command>
-               <!-- Enable an "open file" action in a single sourced application  -->  
-               <command
-                       defaultHandler="org.argeo.eclipse.spring.SpringExtensionFactory"
-                       id="org.argeo.eclipse.ui.workbench.openFile"
-                       name="OpenFile">
-                       <commandParameter
-                       id="param.fileName"
-                       name="The name of the file to open (optional)">
-                       </commandParameter>
-            <commandParameter
-                       id="param.fileURI"
-                       name="The URI of this file on the server">
-                       </commandParameter>
-                       <commandParameter
-                       id="param.filePath"
-                       name="The absolute path of this file on the server file system">
-                       </commandParameter>
-               </command>
-       </extension>
-
-       <!-- MENUS --> 
-       <extension point="org.eclipse.ui.menus">
-       <!-- Main tool bar menu -->
-       <menuContribution locationURI="toolbar:org.eclipse.ui.main.toolbar">
-               <toolbar id="org.argeo.security.ui.rap.userToolbar">
-                               <command
-                                       commandId="org.argeo.security.ui.rap.mainMenuCommand"
-                                       icon="icons/home.gif"
-                                       id="org.argeo.security.ui.rap.mainMenu"
-                                       style="pulldown">
-                               </command>
-                               <command commandId="org.eclipse.ui.file.save"/>
-                               <command commandId="org.eclipse.ui.file.saveAll"/>
-                       </toolbar>
-               </menuContribution>
-               
-               <!-- User drop down default menu -->
-               <menuContribution locationURI="menu:org.argeo.security.ui.rap.mainMenu">
-                       <!-- Managed programmatically in the RapActionBarAdvisor to enable 
-                            the display of the current logged-in user id -->
-                       <command
-                               commandId="org.argeo.security.ui.rap.userMenuCommand"
-                               icon="icons/main.gif"
-                               id="org.argeo.security.ui.rap.userMenu">
-                       </command>
-                       <!-- Still unused
-                       <command
-                               commandId="org.eclipse.ui.window.preferences"
-                               icon="icons/preferences.png"/> -->
-               <command
-                               commandId="org.argeo.security.ui.rap.openChangePasswordDialog"
-                               icon="icons/password.gif"
-                               label="%changePassword"/>
-                       <separator
-                               name="org.argeo.security.ui.rap.beforeFile"
-                               visible="true">
-                       </separator>
-                       <command
-                               commandId="org.eclipse.ui.file.closeAll"
-                               icon="icons/closeAll.gif"/>
-                       <command commandId="org.eclipse.ui.file.save"/>
-                       <command commandId="org.eclipse.ui.file.saveAll"/>
-       
-                       <!--<command commandId="org.eclipse.ui.views.showView"/>-->
-               <!--<command commandId="org.eclipse.ui.perspectives.showPerspective"/>-->
-       
-                       <separator
-                               name="org.argeo.security.ui.rap.beforeExit"
-                               visible="true">
-                       </separator>
-                       <command commandId="org.eclipse.ui.file.exit" icon="icons/exit.png"/>
-               </menuContribution>
-       </extension>
-               
-    <!-- SERVICE HANDLERS --> 
-       <extension point="org.eclipse.rap.ui.serviceHandler">
-               <!-- Rap specific service handler to enable file download over the internet-->
-               <serviceHandler
-                       class="org.argeo.eclipse.ui.specific.OpenFileService"
-                       id="org.argeo.security.ui.specific.openFileService">
-               </serviceHandler>
-       </extension>
-    
-    <!-- ACTIVITIES -->
-       <extension
-           point="org.eclipse.ui.activities">
-        <activity
-              description="Anonymous"
-              id="org.argeo.security.ui.rap.anonymousActivity"
-              name="Anonymous">
-                 <enabledWhen>
-                   <with variable="roles">
-                     <iterate ifEmpty="false" operator="or">
-                       <equals value="cn=anonymous,ou=roles,ou=node" />
-                     </iterate>
-                   </with>
-                 </enabledWhen>
-        </activity>
-        <activity
-              description="Not anonymous"
-              id="org.argeo.security.ui.rap.notAnonymousActivity"
-              name="NotAnonymous">
-                 <enabledWhen>
-                       <not>
-                   <with variable="roles">
-                     <iterate ifEmpty="false" operator="or">
-                       <equals value="cn=anonymous,ou=roles,ou=node" />
-                     </iterate>
-                   </with>
-                   </not>
-                 </enabledWhen>
-        </activity>
-               <activityPatternBinding
-              activityId="org.argeo.security.ui.rap.notAnonymousActivity"
-              pattern="org.argeo.security.ui.rap/org.argeo.security.ui.rap.userMenuCommand"/>         
-        <activityPatternBinding
-              activityId="org.argeo.security.ui.rap.notAnonymousActivity"
-              pattern="org.argeo.security.ui.rap/org.eclipse.ui.window.preferences"/>
-        <activityPatternBinding
-              activityId="org.argeo.security.ui.rap.notAnonymousActivity"
-              pattern="org.argeo.security.ui.rap/org.argeo.security.ui.rap.openChangePasswordDialog"/>
-     </extension>
-    
-    <!-- BRANDINGS --> 
-     <extension
-         point="org.eclipse.rap.ui.branding">
-       <branding
-                       id="org.argeo.security.ui.rap.defaultBranding"
-            themeId="org.eclipse.rap.rwt.theme.Default"
-            title="Argeo Web UI"
-            favicon="branding/favicon.ico">
-       </branding>
-       <!-- we need a servlet with this name j_spring_security_logout
-                for the logout filter -->
-       <branding
-                       id="org.argeo.security.ui.rap.logoutBranding"
-            title="Argeo Logout"
-            favicon="branding/favicon.ico"
-            body="branding/empty.html">
-       </branding>
-       </extension>
-</plugin>
diff --git a/org.argeo.security.ui.rap/pom.xml b/org.argeo.security.ui.rap/pom.xml
deleted file mode 100644 (file)
index 5f8c994..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-       <modelVersion>4.0.0</modelVersion>
-       <parent>
-               <groupId>org.argeo.commons</groupId>
-               <version>2.1.46-SNAPSHOT</version>
-               <artifactId>argeo-commons</artifactId>
-               <relativePath>..</relativePath>
-       </parent>
-       <artifactId>org.argeo.security.ui.rap</artifactId>
-       <name>Commons CMS Workbench RAP</name>
-       <packaging>jar</packaging>
-       <dependencies>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.util</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.security.ui</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.eclipse.ui.rap</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.cms</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-       </dependencies>
-</project>
\ No newline at end of file
diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/AnonymousEntryPoint.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/AnonymousEntryPoint.java
deleted file mode 100644 (file)
index 04b6f0a..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.rap;
-
-import java.security.PrivilegedAction;
-
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.auth.AuthConstants;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.application.EntryPoint;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.PlatformUI;
-
-/**
- * RAP entry point which authenticates the subject as anonymous, for public
- * unauthenticated access.
- */
-public class AnonymousEntryPoint implements EntryPoint {
-       private final static Log log = LogFactory.getLog(AnonymousEntryPoint.class);
-
-       /**
-        * How many seconds to wait before invalidating the session if the user has
-        * not yet logged in.
-        */
-       private Integer sessionTimeout = 5 * 60;
-
-       @Override
-       public int createUI() {
-               RWT.getRequest().getSession().setMaxInactiveInterval(sessionTimeout);
-
-               // if (log.isDebugEnabled())
-               // log.debug("Anonymous THREAD=" + Thread.currentThread().getId()
-               // + ", sessionStore=" + RWT.getSessionStore().getId());
-
-               final Display display = PlatformUI.createDisplay();
-               Subject subject = new Subject();
-
-               final LoginContext loginContext;
-               try {
-                       loginContext = new LoginContext(AuthConstants.LOGIN_CONTEXT_ANONYMOUS,
-                                       subject);
-                       loginContext.login();
-               } catch (LoginException e1) {
-                       throw new CmsException("Cannot initialize login context", e1);
-               }
-
-               // identify after successful login
-               if (log.isDebugEnabled())
-                       log.debug("Authenticated " + subject);
-               final String username = subject.getPrincipals().iterator().next()
-                               .getName();
-
-               // Logout callback when the display is disposed
-               display.disposeExec(new Runnable() {
-                       public void run() {
-                               log.debug("Display disposed");
-                               logout(loginContext, username);
-                       }
-               });
-
-               //
-               // RUN THE WORKBENCH
-               //
-               Integer returnCode = null;
-               try {
-                       returnCode = Subject.doAs(subject, new PrivilegedAction<Integer>() {
-                               public Integer run() {
-                                       RapWorkbenchAdvisor workbenchAdvisor = new RapWorkbenchAdvisor(
-                                                       null);
-                                       int result = PlatformUI.createAndRunWorkbench(display,
-                                                       workbenchAdvisor);
-                                       return new Integer(result);
-                               }
-                       });
-                       logout(loginContext, username);
-                       if (log.isTraceEnabled())
-                               log.trace("Return code " + returnCode);
-               } finally {
-                       display.dispose();
-               }
-               return 1;
-       }
-
-       private void logout(LoginContext loginContext, String username) {
-               try {
-                       loginContext.logout();
-                       log.info("Logged out " + (username != null ? username : "")
-                                       + " (THREAD=" + Thread.currentThread().getId() + ")");
-               } catch (LoginException e) {
-                       log.error("Erorr when logging out", e);
-               }
-       }
-}
diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapActionBarAdvisor.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapActionBarAdvisor.java
deleted file mode 100644 (file)
index 14bcee1..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.rap;
-
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.security.ui.commands.OpenHomePerspective;
-import org.eclipse.core.commands.Category;
-import org.eclipse.core.commands.Command;
-import org.eclipse.jface.action.ICoolBarManager;
-import org.eclipse.jface.action.IMenuManager;
-import org.eclipse.jface.action.IToolBarManager;
-import org.eclipse.jface.action.ToolBarManager;
-import org.eclipse.swt.SWT;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.actions.ActionFactory;
-import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
-import org.eclipse.ui.application.ActionBarAdvisor;
-import org.eclipse.ui.application.IActionBarConfigurer;
-import org.eclipse.ui.commands.ICommandService;
-
-/** Eclipse rap specific action bar advisor */
-public class RapActionBarAdvisor extends ActionBarAdvisor {
-       private final static String ID_BASE = "org.argeo.security.ui.rap";
-       // private final static Log log = LogFactory
-       // .getLog(SecureActionBarAdvisor.class);
-
-       /** Null means anonymous */
-       private String username = null;
-
-       // private IAction logoutAction;
-       // private IWorkbenchAction openPerspectiveDialogAction;
-       // private IWorkbenchAction showViewMenuAction;
-       // private IWorkbenchAction preferences;
-       private IWorkbenchAction saveAction;
-       private IWorkbenchAction saveAllAction;
-
-       // private IWorkbenchAction closeAllAction;
-
-       public RapActionBarAdvisor(IActionBarConfigurer configurer, String username) {
-               super(configurer);
-               this.username = username;
-       }
-
-       protected void makeActions(IWorkbenchWindow window) {
-               // preferences = ActionFactory.PREFERENCES.create(window);
-               // register(preferences);
-               // openPerspectiveDialogAction = ActionFactory.OPEN_PERSPECTIVE_DIALOG
-               // .create(window);
-               // register(openPerspectiveDialogAction);
-               // showViewMenuAction = ActionFactory.SHOW_VIEW_MENU.create(window);
-               // register(showViewMenuAction);
-               //
-               // // logout
-               // logoutAction = ActionFactory.QUIT.create(window);
-               // // logoutAction = createLogoutAction();
-               // register(logoutAction);
-               //
-               // Save semantics
-               saveAction = ActionFactory.SAVE.create(window);
-               register(saveAction);
-               saveAllAction = ActionFactory.SAVE_ALL.create(window);
-               register(saveAllAction);
-               // closeAllAction = ActionFactory.CLOSE_ALL.create(window);
-               // register(closeAllAction);
-
-       }
-
-       protected void fillMenuBar(IMenuManager menuBar) {
-               // MenuManager fileMenu = new MenuManager("&File",
-               // IWorkbenchActionConstants.M_FILE);
-               // MenuManager editMenu = new MenuManager("&Edit",
-               // IWorkbenchActionConstants.M_EDIT);
-               // MenuManager windowMenu = new MenuManager("&Window",
-               // IWorkbenchActionConstants.M_WINDOW);
-               //
-               // menuBar.add(fileMenu);
-               // menuBar.add(editMenu);
-               // menuBar.add(windowMenu);
-               // // Add a group marker indicating where action set menus will appear.
-               // menuBar.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
-               //
-               // // File
-               // fileMenu.add(saveAction);
-               // fileMenu.add(saveAllAction);
-               // fileMenu.add(closeAllAction);
-               // fileMenu.add(new
-               // GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
-               // fileMenu.add(new Separator());
-               // fileMenu.add(logoutAction);
-               //
-               // // Edit
-               // editMenu.add(preferences);
-               //
-               // // Window
-               // windowMenu.add(openPerspectiveDialogAction);
-               // windowMenu.add(showViewMenuAction);
-       }
-
-       @Override
-       protected void fillCoolBar(ICoolBarManager coolBar) {
-               // Add a command which label is the display name of the current
-               // logged-in user
-               if (username != null) {
-                       ICommandService cmdService = (ICommandService) getActionBarConfigurer()
-                                       .getWindowConfigurer().getWorkbenchConfigurer()
-                                       .getWorkbench().getService(ICommandService.class);
-                       Category userMenus = cmdService.getCategory(ID_BASE + ".userMenus");
-                       if (!userMenus.isDefined())
-                               userMenus.define("User Menus", "User related menus");
-                       Command userMenu = cmdService.getCommand(ID_BASE
-                                       + ".userMenuCommand");
-                       if (userMenu.isDefined())
-                               userMenu.undefine();
-                       userMenu.define(CurrentUser.getDisplayName(), "User menu actions",
-                                       userMenus);
-                       // userMenu.define(username, "User menu actions", userMenus);
-                       
-                       userMenu.setHandler(new OpenHomePerspective());
-
-                       // userToolbar.add(new UserMenuAction());
-                       // coolBar.add(userToolbar);
-               } else {// anonymous
-                       IToolBarManager userToolbar = new ToolBarManager(SWT.FLAT
-                                       | SWT.RIGHT);
-                       // userToolbar.add(logoutAction);
-                       coolBar.add(userToolbar);
-               }
-               // IToolBarManager saveToolbar = new ToolBarManager(SWT.FLAT |
-               // SWT.RIGHT);
-               // saveToolbar.add(saveAction);
-               // saveToolbar.add(saveAllAction);
-               // coolBar.add(saveToolbar);
-       }
-
-       // class UserMenuAction extends Action implements IWorkbenchAction {
-       //
-       // public UserMenuAction() {
-       // super(username, IAction.AS_DROP_DOWN_MENU);
-       // // setMenuCreator(new UserMenu());
-       // }
-       //
-       // @Override
-       // public String getId() {
-       // return "org.argeo.security.ui.rap.userMenu";
-       // }
-       //
-       // @Override
-       // public void dispose() {
-       // }
-       //
-       // }
-
-       // class UserMenu implements IMenuCreator {
-       // private Menu menu;
-       //
-       // public Menu getMenu(Control parent) {
-       // Menu menu = new Menu(parent);
-       // addActionToMenu(menu, logoutAction);
-       // return menu;
-       // }
-       //
-       // private void addActionToMenu(Menu menu, IAction action) {
-       // ActionContributionItem item = new ActionContributionItem(action);
-       // item.fill(menu, -1);
-       // }
-       //
-       // public void dispose() {
-       // if (menu != null) {
-       // menu.dispose();
-       // }
-       // }
-       //
-       // public Menu getMenu(Menu parent) {
-       // // Not use
-       // return null;
-       // }
-       //
-       // }
-
-       // protected IAction createLogoutAction() {
-       // Subject subject = Subject.getSubject(AccessController.getContext());
-       // final String username = subject.getPrincipals().iterator().next()
-       // .getName();
-       //
-       // IAction logoutAction = new Action() {
-       // public String getId() {
-       // return SecureRapActivator.ID + ".logoutAction";
-       // }
-       //
-       // public String getText() {
-       // return "Logout " + username;
-       // }
-       //
-       // public void run() {
-       // // try {
-       // // Subject subject = SecureRapActivator.getLoginContext()
-       // // .getSubject();
-       // // String subjectStr = subject.toString();
-       // // subject.getPrincipals().clear();
-       // // SecureRapActivator.getLoginContext().logout();
-       // // log.info(subjectStr + " logged out");
-       // // } catch (LoginException e) {
-       // // log.error("Error when logging out", e);
-       // // }
-       // // SecureEntryPoint.logout(username);
-       // // PlatformUI.getWorkbench().close();
-       // // try {
-       // // RWT.getRequest().getSession().setMaxInactiveInterval(1);
-       // // } catch (Exception e) {
-       // // if (log.isTraceEnabled())
-       // // log.trace("Error when invalidating session", e);
-       // // }
-       // }
-       //
-       // };
-       // return logoutAction;
-       // }
-
-}
diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWindowAdvisor.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWindowAdvisor.java
deleted file mode 100644 (file)
index 05f4787..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.rap;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Listener;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.actions.ActionFactory;
-import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
-import org.eclipse.ui.application.ActionBarAdvisor;
-import org.eclipse.ui.application.IActionBarConfigurer;
-import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
-import org.eclipse.ui.application.WorkbenchWindowAdvisor;
-
-/** Eclipse RAP specific window advisor */
-public class RapWindowAdvisor extends WorkbenchWindowAdvisor {
-
-       private String username;
-
-       public RapWindowAdvisor(IWorkbenchWindowConfigurer configurer,
-                       String username) {
-               super(configurer);
-               this.username = username;
-       }
-
-       @Override
-       public ActionBarAdvisor createActionBarAdvisor(
-                       IActionBarConfigurer configurer) {
-               return new RapActionBarAdvisor(configurer, username);
-       }
-
-       public void preWindowOpen() {
-               IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
-               configurer.setShowCoolBar(true);
-               configurer.setShowMenuBar(false);
-               configurer.setShowStatusLine(false);
-               configurer.setShowPerspectiveBar(true);
-               configurer.setTitle("Argeo Web UI"); //$NON-NLS-1$
-               // Full screen, see
-               // http://wiki.eclipse.org/RAP/FAQ#How_to_create_a_fullscreen_application
-               configurer.setShellStyle(SWT.NO_TRIM);
-               Rectangle bounds = Display.getCurrent().getBounds();
-               configurer.setInitialSize(new Point(bounds.width, bounds.height));
-
-               // Handle window resize in Rap 2.1+ see
-               // https://bugs.eclipse.org/bugs/show_bug.cgi?id=417254
-               Display.getCurrent().addListener(SWT.Resize, new Listener() {
-                       private static final long serialVersionUID = 2970912561866704526L;
-
-                       @Override
-                       public void handleEvent(Event event) {
-                               Rectangle bounds = event.display.getBounds();
-                               IWorkbenchWindow iww = getWindowConfigurer().getWindow()
-                                               .getWorkbench().getActiveWorkbenchWindow();
-                               iww.getShell().setBounds(bounds);
-                       }
-               });
-       }
-
-       @Override
-       public void postWindowCreate() {
-               Shell shell = getWindowConfigurer().getWindow().getShell();
-               shell.setMaximized(true);
-       }
-
-       @Override
-       public void postWindowOpen() {
-               String defaultPerspective = getWindowConfigurer()
-                               .getWorkbenchConfigurer().getWorkbench()
-                               .getPerspectiveRegistry().getDefaultPerspective();
-               if (defaultPerspective == null) {
-                       IWorkbenchWindow window = getWindowConfigurer().getWindow();
-                       if (window == null)
-                               return;
-
-                       IWorkbenchAction openPerspectiveDialogAction = ActionFactory.OPEN_PERSPECTIVE_DIALOG
-                                       .create(window);
-                       openPerspectiveDialogAction.run();
-               }
-       }
-
-}
diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java
deleted file mode 100644 (file)
index edde41f..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.rap;
-
-import org.eclipse.ui.IPerspectiveDescriptor;
-import org.eclipse.ui.application.IWorkbenchConfigurer;
-import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
-import org.eclipse.ui.application.WorkbenchAdvisor;
-import org.eclipse.ui.application.WorkbenchWindowAdvisor;
-
-/** Eclipse RAP specific workbench advisor */
-public class RapWorkbenchAdvisor extends WorkbenchAdvisor {
-       public final static String INITIAL_PERSPECTIVE_PROPERTY = "org.argeo.security.ui.initialPerspective";
-       public final static String SAVE_AND_RESTORE_PROPERTY = "org.argeo.security.ui.saveAndRestore";
-
-       private String initialPerspective = System.getProperty(
-                       INITIAL_PERSPECTIVE_PROPERTY, null);
-
-       private String username;
-
-       public RapWorkbenchAdvisor(String username) {
-               this.username = username;
-       }
-
-       @Override
-       public void initialize(IWorkbenchConfigurer configurer) {
-               super.initialize(configurer);
-               Boolean saveAndRestore = Boolean.parseBoolean(System.getProperty(
-                               SAVE_AND_RESTORE_PROPERTY, "false"));
-               configurer.setSaveAndRestore(saveAndRestore);
-       }
-
-       public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(
-                       IWorkbenchWindowConfigurer configurer) {
-               return new RapWindowAdvisor(configurer, username);
-       }
-
-       public String getInitialWindowPerspectiveId() {
-               if (initialPerspective != null) {
-                       // check whether this user can see the declared perspective
-                       // (typically the perspective won't be listed if this user doesn't
-                       // have the right to see it)
-                       IPerspectiveDescriptor pd = getWorkbenchConfigurer().getWorkbench()
-                                       .getPerspectiveRegistry()
-                                       .findPerspectiveWithId(initialPerspective);
-                       if (pd == null)
-                               return null;
-               }
-               return initialPerspective;
-       }
-}
diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWorkbenchLogin.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/RapWorkbenchLogin.java
deleted file mode 100644 (file)
index 7bf487a..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-package org.argeo.security.ui.rap;
-
-import java.security.PrivilegedAction;
-import java.util.Locale;
-
-import javax.security.auth.Subject;
-import javax.security.auth.x500.X500Principal;
-
-import org.argeo.cms.CmsMsg;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.util.LoginEntryPoint;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.PlatformUI;
-
-public class RapWorkbenchLogin extends LoginEntryPoint {
-       // private final static Log log =
-       // LogFactory.getLog(RapWorkbenchLogin.class);
-
-       /** Override to provide an application specific workbench advisor */
-       protected RapWorkbenchAdvisor createRapWorkbenchAdvisor(String username) {
-               return new RapWorkbenchAdvisor(username);
-       }
-
-       @Override
-       public int createUI() {
-               JavaScriptExecutor jsExecutor = RWT.getClient().getService(
-                               JavaScriptExecutor.class);
-               int returnCode;
-               try {
-                       returnCode = super.createUI();
-               } finally {
-                       // always reload
-                       jsExecutor.execute("location.reload()");
-               }
-               return returnCode;
-       }
-
-       @Override
-       protected int postLogin() {
-               final Display display = Display.getCurrent();
-               Subject subject = getSubject();
-               if (subject.getPrincipals(X500Principal.class).isEmpty()) {
-                       RWT.getClient().getService(JavaScriptExecutor.class)
-                                       .execute("location.reload()");
-               }
-               //
-               // RUN THE WORKBENCH
-               //
-               Integer returnCode = null;
-               try {
-                       returnCode = Subject.doAs(getSubject(),
-                                       new PrivilegedAction<Integer>() {
-                                               public Integer run() {
-                                                       int result = createAndRunWorkbench(display,
-                                                                       CurrentUser.getUsername(getSubject()));
-                                                       return new Integer(result);
-                                               }
-                                       });
-                       // explicit workbench closing
-                       logout();
-               } finally {
-                       display.dispose();
-               }
-               return returnCode;
-       }
-
-       protected int createAndRunWorkbench(Display display, String username) {
-               RapWorkbenchAdvisor workbenchAdvisor = createRapWorkbenchAdvisor(username);
-               return PlatformUI.createAndRunWorkbench(display, workbenchAdvisor);
-       }
-
-       @Override
-       protected void extendsCredentialsBlock(Composite credentialsBlock,
-                       Locale selectedLocale, SelectionListener loginSelectionListener) {
-               Button loginButton = new Button(credentialsBlock, SWT.PUSH);
-               loginButton.setText(CmsMsg.login.lead(selectedLocale));
-               loginButton.setLayoutData(CmsUtils.fillWidth());
-               loginButton.addSelectionListener(loginSelectionListener);
-       }
-
-       @Override
-       protected Display createDisplay() {
-               return PlatformUI.createDisplay();
-       }
-
-}
diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java
deleted file mode 100644 (file)
index a681527..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.rap;
-
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-
-import javax.security.auth.Subject;
-import javax.security.auth.login.CredentialNotFoundException;
-import javax.security.auth.login.FailedLoginException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import javax.security.auth.x500.X500Principal;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.cms.auth.ThreadDeathLoginException;
-import org.argeo.cms.widgets.auth.DefaultLoginDialog;
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.application.EntryPoint;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.PlatformUI;
-
-/**
- * RAP entry point with login capabilities. Once the user has been
- * authenticated, the workbench is run as a privileged action by the related
- * subject.
- */
-@Deprecated
-public class SecureEntryPoint implements EntryPoint {
-       final static String ACCESS_CONTROL_CONTEXT = "org.argeo.node.accessControlContext";
-       private final static Log log = LogFactory.getLog(SecureEntryPoint.class);
-
-       /**
-        * How many seconds to wait before invalidating the session if the user has
-        * not yet logged in.
-        */
-       private Integer loginTimeout = 1 * 60;
-       // TODO make it configurable
-       /** Default session timeout is 8 hours (European working day length) */
-       private Integer sessionTimeout = 8 * 60 * 60;
-
-       /** Override to provide an application specific workbench advisor */
-       protected RapWorkbenchAdvisor createRapWorkbenchAdvisor(String username) {
-               return new RapWorkbenchAdvisor(username);
-       }
-
-       @Override
-       public final int createUI() {
-               // Short login timeout so that the modal dialog login doesn't hang
-               // around too long
-               RWT.getRequest().getSession().setMaxInactiveInterval(loginTimeout);
-
-               final Display display = PlatformUI.createDisplay();
-
-               // load context from session
-               HttpServletRequest httpRequest = RWT.getRequest();
-               final HttpSession httpSession = httpRequest.getSession();
-               AccessControlContext acc = (AccessControlContext) httpSession
-                               .getAttribute(ACCESS_CONTROL_CONTEXT);
-
-               final Subject subject;
-               if (acc != null
-                               && Subject.getSubject(acc).getPrincipals(X500Principal.class)
-                                               .size() == 1) {
-                       subject = Subject.getSubject(acc);
-               } else {
-                       subject = new Subject();
-
-                       final LoginContext loginContext;
-                       DefaultLoginDialog callbackHandler;
-                       try {
-                               callbackHandler = new DefaultLoginDialog(
-                                               display.getActiveShell());
-                               loginContext = new LoginContext(
-                                               AuthConstants.LOGIN_CONTEXT_USER, subject,
-                                               callbackHandler);
-                       } catch (LoginException e1) {
-                               throw new CmsException("Cannot initialize login context", e1);
-                       }
-
-                       tryLogin: while (subject.getPrincipals(X500Principal.class).size() == 0) {
-                               try {
-                                       loginContext.login();
-                                       if (subject.getPrincipals(X500Principal.class).size() == 0)
-                                               throw new CmsException("Login succeeded but no auth");// fatal
-
-                                       // add thread locale to RWT session
-                                       // if (log.isTraceEnabled())
-                                       // log.trace("Locale " + LocaleUtils.threadLocale.get());
-                                       // RWT.setLocale(LocaleUtils.threadLocale.get());
-
-                                       // once the user is logged in, longer session timeout
-                                       RWT.getRequest().getSession()
-                                                       .setMaxInactiveInterval(sessionTimeout);
-
-                                       if (log.isDebugEnabled())
-                                               log.debug("Authenticated " + subject);
-                               } catch (FailedLoginException e) {
-                                       MessageDialog.openInformation(display.getActiveShell(),
-                                                       "Bad Credentials", e.getMessage());
-                                       // retry login
-                                       continue tryLogin;
-                               } catch (CredentialNotFoundException e) {
-                                       MessageDialog.openInformation(display.getActiveShell(),
-                                                       "No Credentials", e.getMessage());
-                                       // retry login
-                                       continue tryLogin;
-                               } catch (LoginException e) {
-                                       callbackHandler.getShell().dispose();
-                                       return processLoginDeath(display, e);
-                               }
-                       }
-               }
-               final String username = subject.getPrincipals(X500Principal.class)
-                               .iterator().next().getName();
-               // Logout callback when the display is disposed
-               display.disposeExec(new Runnable() {
-                       public void run() {
-                               if (log.isTraceEnabled())
-                                       log.trace("Display disposed");
-                               try {
-                                       LoginContext loginContext = new LoginContext(
-                                                       AuthConstants.LOGIN_CONTEXT_USER, subject);
-                                       loginContext.logout();
-                               } catch (LoginException e) {
-                                       log.error("Error when logging out", e);
-                               }
-                       }
-               });
-
-               //
-               // RUN THE WORKBENCH
-               //
-               Integer returnCode = null;
-               try {
-                       returnCode = Subject.doAs(subject, new PrivilegedAction<Integer>() {
-                               public Integer run() {
-                                       // add security context to session
-                                       httpSession.setAttribute(ACCESS_CONTROL_CONTEXT,
-                                                       AccessController.getContext());
-
-                                       // start workbench
-                                       RapWorkbenchAdvisor workbenchAdvisor = createRapWorkbenchAdvisor(username);
-                                       int result = PlatformUI.createAndRunWorkbench(display,
-                                                       workbenchAdvisor);
-                                       return new Integer(result);
-                               }
-                       });
-                       // Explicit exit from workbench
-                       fullLogout(subject, username);
-               } finally {
-                       display.dispose();
-               }
-               return returnCode;
-       }
-
-       private Integer processLoginDeath(Display display, Throwable e) {
-               // check thread death
-               ThreadDeath td = wasCausedByThreadDeath(e);
-               if (td != null) {
-                       display.dispose();
-                       throw td;
-               }
-               if (!display.isDisposed()) {
-                       ErrorFeedback.show("Unexpected exception during authentication", e);
-                       // this was not just bad credentials or death thread
-                       RWT.getRequest().getSession().setMaxInactiveInterval(1);
-                       display.dispose();
-                       return -1;
-               } else {
-                       throw new CmsException(
-                                       "Unexpected exception during authentication", e);
-               }
-
-       }
-
-       /**
-        * If there is a {@link ThreadDeath} in the root causes, rethrow it
-        * (important for RAP cleaning mechanism)
-        */
-       protected ThreadDeath wasCausedByThreadDeath(Throwable t) {
-               if (t instanceof ThreadDeath)
-                       return (ThreadDeath) t;
-               if (t instanceof ThreadDeathLoginException)
-                       return ((ThreadDeathLoginException) t).getThreadDeath();
-               if (t.getCause() != null)
-                       return wasCausedByThreadDeath(t.getCause());
-               else
-                       return null;
-       }
-
-       private void fullLogout(Subject subject, String username) {
-               try {
-                       LoginContext loginContext = new LoginContext(
-                                       AuthConstants.LOGIN_CONTEXT_USER, subject);
-                       loginContext.logout();
-                       HttpServletRequest httpRequest = RWT.getRequest();
-                       HttpSession httpSession = httpRequest.getSession();
-                       httpSession.setAttribute(ACCESS_CONTROL_CONTEXT, null);
-                       RWT.getRequest().getSession().setMaxInactiveInterval(1);
-                       log.info("Logged out " + (username != null ? username : "")
-                                       + " (THREAD=" + Thread.currentThread().getId() + ")");
-               } catch (LoginException e) {
-                       log.error("Error when logging out", e);
-               }
-       }
-}
diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureRapActivator.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureRapActivator.java
deleted file mode 100644 (file)
index b3d7c23..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.rap;
-
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-
-/** Configure Equinox login context from the bundle context. */
-public class SecureRapActivator implements BundleActivator {
-       public final static String ID = "org.argeo.security.ui.rap";
-
-       private static BundleContext bundleContext;
-
-       public void start(BundleContext bc) throws Exception {
-               bundleContext = bc;
-       }
-
-       public void stop(BundleContext context) throws Exception {
-               bundleContext = null;
-       }
-
-       public static BundleContext getBundleContext() {
-               return bundleContext;
-       }
-}
diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/commands/OpenHome.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/commands/OpenHome.java
deleted file mode 100644 (file)
index 37ebe35..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.rap.commands;
-
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.argeo.eclipse.ui.workbench.CommandUtils;
-import org.argeo.security.ui.UserHomePerspective;
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.ui.WorkbenchException;
-import org.eclipse.ui.handlers.HandlerUtil;
-
-/** Default action of the user menu */
-public class OpenHome extends AbstractHandler {
-       private final static String PROP_OPEN_HOME_CMD_ID = "org.argeo.ui.openHomeCommandId";
-
-       public Object execute(ExecutionEvent event) throws ExecutionException {
-
-               String defaultCmdId = System.getProperty(PROP_OPEN_HOME_CMD_ID, "");
-               if (!"".equals(defaultCmdId.trim()))
-                       CommandUtils.callCommand(defaultCmdId);
-               else {
-                       try {
-                               HandlerUtil.getActiveSite(event).getWorkbenchWindow()
-                                               .openPage(UserHomePerspective.ID, null);
-                       } catch (WorkbenchException e) {
-                               ErrorFeedback.show("Cannot open home perspective", e);
-                       }
-               }
-               return null;
-       }
-}
diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/commands/UserMenu.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/commands/UserMenu.java
deleted file mode 100644 (file)
index 9867430..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.ui.rap.commands;
-
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-
-/** Default action of the user menu */
-public class UserMenu extends AbstractHandler {
-
-       @Override
-       public Object execute(ExecutionEvent event) throws ExecutionException {
-               return null;
-       }
-
-}
index 5672e9b93ec21ce21e59465c403260091284669f..beb37408e6d9ac1f568a3807339538c0a95395c0 100644 (file)
@@ -8,7 +8,7 @@
                <relativePath>..</relativePath>
        </parent>
        <artifactId>org.argeo.security.ui</artifactId>
-       <name>Commons CMS Workbench</name>
+       <name>CMS Workbench</name>
        <packaging>jar</packaging>
        <dependencies>
                <dependency>
                        <artifactId>org.argeo.cms</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.security.core</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
+<!--           <dependency> -->
+<!--                   <groupId>org.argeo.commons</groupId> -->
+<!--                   <artifactId>org.argeo.security.core</artifactId> -->
+<!--                   <version>2.1.46-SNAPSHOT</version> -->
+<!--           </dependency> -->
                <dependency>
                        <groupId>org.argeo.commons</groupId>
                        <artifactId>org.argeo.eclipse.ui.workbench</artifactId>
diff --git a/org.argeo.server.jcr/.classpath b/org.argeo.server.jcr/.classpath
deleted file mode 100644 (file)
index a8a298a..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="src" path="ext/test"/>
-       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
-       <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/org.argeo.server.jcr/.project b/org.argeo.server.jcr/.project
deleted file mode 100644 (file)
index 977218c..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.server.jcr</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.pde.PluginNature</nature>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-       </natures>
-</projectDescription>
diff --git a/org.argeo.server.jcr/bnd.bnd b/org.argeo.server.jcr/bnd.bnd
deleted file mode 100644 (file)
index 5645d30..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-Import-Package: junit.framework;resolution:=optional,\
-org.xml.sax;version="0.0.0",\
-org.springframework.core;resolution:=optional,\
-org.springframework.core.io;resolution:=optional,\
-org.springframework.*;resolution:=optional,\
-org.apache.jackrabbit.*;resolution:=optional,\
-org.apache.jackrabbit.webdav.jcr;resolution:=optional,\
-org.apache.jackrabbit.webdav.server;resolution:=optional,\
-org.h2;resolution:=optional,\
-org.postgresql;resolution:=optional,\
-*
-Export-Package: org.argeo.jcr.*, org.argeo.jackrabbit.*
\ No newline at end of file
diff --git a/org.argeo.server.jcr/build.properties b/org.argeo.server.jcr/build.properties
deleted file mode 100644 (file)
index f4baf37..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-source.. = src/,\
-           ext/test/
-output.. = bin/
-bin.includes = META-INF/,\
-               .
-additional.bundles = org.junit,\
-                     org.apache.jackrabbit.core,\
-                     javax.jcr,\
-                     org.apache.jackrabbit.api,\
-                     org.apache.jackrabbit.data,\
-                     org.apache.jackrabbit.jcr.commons,\
-                     org.apache.jackrabbit.spi,\
-                     org.apache.jackrabbit.spi.commons,\
-                     org.slf4j.api,\
-                     org.slf4j.commons.logging,\
-                     org.slf4j.log4j12,\
-                     org.apache.log4j,\
-                     org.apache.commons.collections,\
-                     EDU.oswego.cs.dl.util.concurrent,\
-                     org.apache.lucene,\
-                     org.apache.tika.core,\
-                     org.apache.commons.dbcp,\
-                     org.apache.commons.pool
diff --git a/org.argeo.server.jcr/ext/test/log4j.properties b/org.argeo.server.jcr/ext/test/log4j.properties
deleted file mode 100644 (file)
index 3d75289..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-log4j.rootLogger=WARN, console
-
-## Levels
-log4j.logger.org.argeo=DEBUG
-log4j.logger.org.apache.jackrabbit=OFF
-
-## Appenders
-# console is set to be a ConsoleAppender.
-log4j.appender.console=org.apache.log4j.ConsoleAppender
-
-# console uses PatternLayout.
-log4j.appender.console.layout=org.apache.log4j.PatternLayout
-#log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n
-log4j.appender.console.layout.ConversionPattern=%m%n
diff --git a/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/DocBookModelTest.java b/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/DocBookModelTest.java
deleted file mode 100644 (file)
index 5af20ba..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-package org.argeo.jcr.docbook;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.commons.cnd.CndImporter;
-import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase;
-import org.argeo.jcr.JcrUtils;
-
-public class DocBookModelTest extends AbstractJackrabbitTestCase {
-       private final static Log log = LogFactory.getLog(DocBookModelTest.class);
-
-       public void testLoadWikipediaSample() throws Exception {
-               importXml("WikipediaSample.dbk.xml");
-       }
-
-       public void XXXtestLoadHowTo() throws Exception {
-               importXml("howto.xml", false);
-       }
-
-       protected void importXml(String res) throws Exception {
-               importXml(res, true);
-       }
-
-       protected void importXml(String res, Boolean mini) throws Exception {
-               byte[] bytes;
-               try (InputStream in = getClass().getResourceAsStream(res)) {
-                       bytes = IOUtils.toByteArray(in);
-               }
-
-               {// cnd
-                       long begin = System.currentTimeMillis();
-                       if (mini) {
-                               InputStreamReader reader = new InputStreamReader(getClass()
-                                               .getResourceAsStream(
-                                                               "/org/argeo/jcr/docbook/docbook.cnd"));
-                               CndImporter.registerNodeTypes(reader, session());
-                               reader.close();
-                       } else {
-                               InputStreamReader reader = new InputStreamReader(getClass()
-                                               .getResourceAsStream(
-                                                               "/org/argeo/jcr/docbook/docbook-full.cnd"));
-                               CndImporter.registerNodeTypes(reader, session());
-                               reader.close();
-                       }
-                       long duration = System.currentTimeMillis() - begin;
-                       if (log.isDebugEnabled())
-                               log.debug(" CND loaded in " + duration + " ms");
-               }
-
-               String testPath = "/" + res;
-               // if (mini)
-               JcrUtils.mkdirs(session(), testPath, "dbk:set");
-               // else
-               // JcrUtils.mkdirs(session(), testPath, "dbk:book");
-
-               DocBookModel model = new DocBookModel(session());
-               try (InputStream in = new ByteArrayInputStream(bytes)) {
-                       long begin = System.currentTimeMillis();
-                       model.importXml(testPath, in);
-                       long duration = System.currentTimeMillis() - begin;
-                       if (log.isDebugEnabled())
-                               log.debug("Imported " + res + " " + (bytes.length / 1024l)
-                                               + " kB in " + duration + " ms ("
-                                               + (bytes.length / duration) + " B/ms)");
-               }
-
-               saveSession();
-               // JcrUtils.debug(session().getRootNode());
-
-               try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
-                       try {
-                               model.exportXml(testPath + "/dbk:book", out);
-                       } catch (Exception e) {
-                               model.exportXml(testPath + "/dbk:article", out);
-                       }
-                       bytes = out.toByteArray();
-
-                       session().logout();
-                       model.setSession(session());
-
-                       // log.debug(new String(bytes));
-                       try (InputStream in = new ByteArrayInputStream(bytes)) {
-                               long begin = System.currentTimeMillis();
-                               model.importXml(testPath, in);
-                               long duration = System.currentTimeMillis() - begin;
-                               if (log.isDebugEnabled())
-                                       log.debug("Re-imported " + res + " "
-                                                       + (bytes.length / 1024l) + " kB in " + duration
-                                                       + " ms (" + (bytes.length / duration) + " B/ms)");
-                       }
-               }
-               saveSession();
-       }
-
-       protected void saveSession() throws RepositoryException {
-               long begin = System.currentTimeMillis();
-               session().save();
-               long duration = System.currentTimeMillis() - begin;
-               if (log.isDebugEnabled())
-                       log.debug(" Session save took " + duration + " ms");
-       }
-
-       // public static Test suite() {
-       // return defaultTestSuite(DocBookModelTest.class);
-       // }
-
-}
diff --git a/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/WikipediaSample.dbk.xml b/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/WikipediaSample.dbk.xml
deleted file mode 100644 (file)
index 29f5b70..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<book xmlns="http://docbook.org/ns/docbook">
-       <title>Very simple book</title>
-       <chapter>
-               <title>Chapter 1</title>
-               <para>Hello world!</para>
-               <para>I hope that your day is proceeding <emphasis>splendidly</emphasis>!</para>
-       </chapter>
-       <chapter>
-               <title>Chapter 2</title>
-               <para>Hello again, world!</para>
-       </chapter>
-</book>
\ No newline at end of file
diff --git a/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/howto.xml b/org.argeo.server.jcr/ext/test/org/argeo/jcr/docbook/howto.xml
deleted file mode 100644 (file)
index b8b022a..0000000
+++ /dev/null
@@ -1,2295 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>  <!-- -*- nxml -*- -->
-<!DOCTYPE book [
-<!ENTITY version "5.0">
-<!--
-<!ENTITY yes "<phrase dbk:role='unicode yes'>✔</phrase>">
-<!ENTITY no "<phrase dbk:role='unicode no'>✘</phrase>">
--->
-<!ENTITY yes "<phrase dbk:role='unicode yes'>YES</phrase>">
-<!ENTITY no "<phrase dbk:role='unicode no'>NO</phrase>">
-]>
-<book xmlns="http://docbook.org/ns/docbook"  xmlns:dbk="http://docbook.org/ns/docbook"
-        xmlns:xl="http://www.w3.org/1999/xlink" xml:lang="en">
-<article>
-<info>
-<title>DocBook V5.0</title>
-<subtitle>The Transition Guide</subtitle>
-
-<authorgroup>
-<author><personname>Jirka Kosek</personname>
-        <email>jirka@kosek.cz</email></author>
-<author><personname>Norman Walsh</personname>
-        <email>ndw@nwalsh.com</email>
-        <contrib>§convert4to5, proofreading</contrib></author>
-<author><personname>Dick Hamilton</personname>
-        <email>rlhamilton@frii.com</email>
-        <contrib>§changes-removed, customization, proofreading</contrib></author>
-<othercredit
-  dbk:class="other"
-  dbk:otherclass="contributor"
-  ><personname>Michael(tm) Smith</personname>
-  <email>smith@sideshowbarker.net</email>
-  <contrib>§dbxsl-ns</contrib>
-</othercredit>
-</authorgroup>
-
-<pubdate>2009-06-16</pubdate>
-<pubdate>2008-02-06</pubdate>
-<pubdate>2007-10-28</pubdate>
-<pubdate>2006-10-22</pubdate>
-<pubdate>2006-05-16</pubdate>
-<pubdate>2006-03-01</pubdate>
-<pubdate>2005-12-28</pubdate>
-<pubdate>2005-10-27</pubdate>
-
-</info>
-
-<para>This document is targeted at DocBook users who are considering
-switching from DocBook V4.x to DocBook V5.0. It describes
-differences between DocBook V4.x and V5.0 and provides some suggestions about
-how to edit and process DocBook V5.0 documents. There is
-also a section devoted to conversion of legacy documents from DocBook
-4.x to DocBook V5.0.</para>
-
-<para>At the time this was written the current version of DocBook V5.0
-was &version;. However, almost all of the information in this document is
-general and applies to any newer version of DocBook V5.0.
-</para>
-
-<section xml:id="introduction">
-<title>Introduction</title>
-
-<para>The differences between DocBook V4.x and V5.0 are quite radical in
-some aspects, but the basic idea behind DocBook is still the same, and
-almost all element names are unchanged. Because of this it is very
-easy to become familiar with DocBook V5.0 if you know any previous version of
-DocBook. You can find a complete list of changes in
-<citation>DB5SPEC</citation>, here we will discuss only the most
-fundamental changes.</para>
-
-<section xml:id="introduction-ns">
-<title>Finally in a namespace</title>
-
-<para>All DocBook V5.0 elements are in the namespace
-<uri>http://docbook.org/ns/docbook</uri>. <acronym>XML<alt>Extensible
-Markup Language</alt></acronym> namespaces are used to distinguish
-between different element sets. In the last few years, almost all new
-XML grammars have used their own namespace. It is easy to
-create compound documents that contain elements from different XML
-vocabularies. DocBook V5.0 is following this design rule. Using
-namespaces in your documents is very easy. Consider this
-simple article marked up in DocBook V4.5:</para>
-
-<programlisting><![CDATA[<article>
-  <title>Sample article</title>
-  <para>This is a really short article.</para>
-</article>]]></programlisting>
-
-<para>The corresponding DocBook V5.0 article will look very similar:</para>
-
-<programlisting><![CDATA[<article xmlns="http://docbook.org/ns/docbook" …>
-  <title>Sample article</title>
-  <para>This is a really short article.</para>
-</article>]]></programlisting>
-
-<para>The only change is the addition of a default namespace declaration
-(<code>xmlns="http://docbook.org/ns/docbook"</code>) on the root
-element. This declaration applies the namespace to the root element and
-all nested elements. Each
-element is now uniquely identified by its local name and namespace.</para>
-
-<note>
-<para>The namespace name <uri>http://docbook.org/ns/docbook</uri> serves
-only as an identifier. This resource is not fetched during processing
-of DocBook documents, and you are not required to have an Internet
-connection during processing. If you access the namespace URI with a browser,
-you will find a short explanatory document about the namespace. In the
-future this document will probably conform to (some version of) RDDL
-and provide pointers to related resources.</para>
-</note>
-
-</section>
-
-<section xml:id="introduction-rng">
-<title>Relaxing with DocBook</title>
-
-<para>For more than a decade, the DocBook schema was defined using a
-DTD. However, DTDs have serious limitations, and DocBook V5.0 is thus
-defined using a very powerful schema language called RELAX NG. Thanks
-to RELAX NG, it is now much easier to create customized versions of
-DocBook, and some content models are now cleaner and more
-precise.</para>
-
-<para>Using RELAX NG has an impact on the document prolog. The following
-example shows the typical prolog of a DocBook V4.x document. The version of
-the DocBook DTD (in this case 4.5) is indicated in the document type
-declaration (!DOCTYPE) which points to a particular version of the
-DTD.</para>
-
-<example xml:id="ex.docbook45">
-<title>DocBook V4.5 document</title>
-<programlisting><![CDATA[<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE article PUBLIC '-//OASIS//DTD DocBook XML V4.5//EN'
-                         'http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd'>
-<article lang="en">
-  <title>Sample article</title>
-  <para>This is a very short article.</para>
-</article>]]></programlisting>
-</example>
-
-<para>In contrast, DocBook V5.0 does not depend on DTDs anymore. This
-mean that there is no document type declaration and the version of DocBook
-used is indicated with the <tag dbk:class="attribute">version</tag>
-attribute instead.</para>
-
-<example xml:id="ex.docbook5">
-<title>DocBook V5.0 document</title>
-<programlisting><![CDATA[<?xml version="1.0" encoding="utf-8"?>
-<article xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
-  <title>Sample article</title>
-  <para>This is a very short article.</para>
-</article>]]></programlisting>
-</example>
-
-<para>As you can see, DocBook V5.0 is built on top of existing XML
-standards as much as possible, for example the <tag
-dbk:class="attribute">lang</tag> attribute is superseded by the standard
-<tag xl:href="http://www.w3.org/TR/REC-xml/#sec-lang-tag"
-dbk:class="attribute">xml:lang</tag> attribute.</para>
-
-<para>Another fundamental change is that there is no direct indication
-of the schema used. Later in this document, you will learn how you can
-specify a schema to be used for document validation.</para>
-
-<note>
-<para>Although we recommend the RELAX NG schema for DocBook
-V5.0, there are also DTD and W3C XML Schema versions available (see <xref
-dbk:linkend="schemas"/>) for tools that do not yet support RELAX NG.</para>
-</note>
-
-</section>
-
-<section xml:id="introduction-why-to-switch">
-<title>Why switch to DocBook V5.0?</title>
-
-<para>The simple answer is <quote>because DocBook V5.0 is the
-future</quote>. Apart from this marketing blurb, there are also more
-technical reasons:</para>
-
-<itemizedlist>
-<listitem>
-<para><emphasis>DocBook V4.x is feature frozen.</emphasis>DocBook V4.5
-is the last version of DocBook in the V4.x series. Any new DocBook
-development, like the addition of new elements, will be done in
-DocBook V5.0. It is only matter of time before useful, new elements
-will be added into DocBook V5.0, but they are not likely to be back
-ported into DocBook V4.x. DocBook V4.x will be in maintenance mode and
-errata will be published if necessary. </para>
-</listitem>
-<listitem>
-<para><emphasis>DocBook V5.0 offers new functionality.</emphasis>
-DocBook V5.0 provides significant improvements over DocBook V4.x. For
-example there is general markup for annotations, a new and flexible
-system for linking, and unified markup for information sections using
-the <tag>info</tag> element.</para>
-</listitem>
-<listitem>
-<para><emphasis>DocBook V5.0 is more extensible.</emphasis> Having
-DocBook V5.0 in a separate namespace allows you to easily mix DocBook
-markup with other XML-based languages like SVG, MathML, XHTML or even
-FooBarML.</para>
-</listitem>
-<listitem>
-<para><emphasis>DocBook V5.0 is easier to customize.</emphasis> RELAX
-NG offers many powerful constructs that make customization much easier
-than it would be using a DTD (see <xref dbk:linkend="customizations"/>).</para>
-</listitem>
-</itemizedlist>
-
-</section>
-
-<section xml:id="introduction-schemas">
-<title>Schema jungle</title>
-
-<para>Schemas for DocBook V5.0 are available in several formats at
-<link xl:href="http://www.oasis-open.org/docbook/xml/&version;/"/> (or the
-mirror at <link xl:href="http://docbook.org/xml/&version;/"/>).
-Only the RELAX NG schema is normative
-and it is preferred over the other schema languages.  However, for your
-convenience there are also DTD and W3C XML Schema versions provided for DocBook
-V5.0. But please note that neither the DTD nor the W3C XML schema are able to
-capture all the constraints of DocBook V5.0. This mean that a
-document that validates against the DTD or XML schema is not necessarily
-valid against the RELAX NG schema and thus may not be a valid
-DocBook V5.0 document. See <xref dbk:linkend="t.schema-comparison"/> for
-summary of constraints that are checked by different schemas.</para>
-
-<para>DTD and W3C XML Schema versions of the DocBook V5.0 grammar are provided
-as a convenience for users who want to use DocBook V5.0 with legacy tools
-that don't support RELAX NG. Authors are encouraged to switch to RELAX
-NG based tools as soon as possible, or at least to validate documents
-against the RELAX NG schema before further processing.</para>
-
-<para>Some document constraints can't be expressed in schema languages
-like RELAX NG or W3C XML Schema. To check for these additional
-constraints DocBook V5.0 uses Schematron.  We recommend that you
-validate your document against both the RELAX NG and
-Schematron schemas.</para>
-
-<table xml:id="t.schema-comparison">
-  <title>Schema Comparison</title>
-  <tgroup dbk:cols="6">
-    <colspec dbk:colwidth="4*"/>
-    <colspec dbk:colwidth="1*" dbk:align="center"/>
-    <colspec dbk:colwidth="1*" dbk:align="center"/>
-    <colspec dbk:colwidth="1*" dbk:align="center"/>
-    <colspec dbk:colwidth="1*" dbk:align="center"/>
-    <colspec dbk:colwidth="1*" dbk:align="center"/>
-    <thead>
-      <row>
-       <entry>Description</entry>
-       <entry>DTD</entry>
-       <entry>W3C XML Schema</entry>
-       <entry>W3C XML Schema + Schematron</entry>
-       <entry>RELAX NG</entry>
-       <entry>RELAX NG + Schematron/NVDL</entry>
-      </row>
-    </thead>
-    <tbody>
-      <row>
-       <entry>Basic document structure</entry>
-       <entry>&yes;</entry>
-       <entry>&yes;</entry>
-       <entry>&yes;</entry>
-       <entry>&yes;</entry>
-       <entry>&yes;</entry>
-      </row>
-      <row>
-       <entry>ID/IDREF datatypes</entry>
-       <entry>&yes;</entry>
-       <entry>&yes;</entry>
-       <entry>&yes;</entry>
-       <entry>&yes;</entry>
-       <entry>&yes;</entry>
-      </row>
-      <row>
-       <entry>Datatypes<footnote>
-         <para>In a very few places RELAX NG specifies datatype
-         like number (mainly for length specifications) or
-         enumeration between <literal>0</literal> and
-         <literal>1</literal>.</para>
-         <para>In general those datatypes can be also supported in
-         W3C XML Schema, but currently this schema is generated
-         from DTD which lacks datatype information.</para>
-       </footnote>
-       </entry>
-       <entry>&no;</entry>
-       <entry>&no;</entry>
-       <entry>&no;</entry>
-       <entry>&yes;</entry>
-       <entry>&yes;</entry>
-      </row>
-      <row>
-       <entry>Co-occurrences<footnote>
-       <para>RELAX NG grammar enforces exclusivity of several
-       elements. For example if you have <tag>title</tag> inside
-       <tag>info</tag> then it is not allowed to have another
-       <tag>title</tag> outside <tag>info</tag>. Similarly,
-       models of HTML and CALS tables are separated and validated
-       properly, where in DTD and WXS only union of both models is
-       available.</para>
-       <para>On other places co-occurrences enforces particular
-       content model based on presence of specific attribute or
-       attribute value.</para>
-       <para>Please also note that in theory co-occurences can be
-       validated using Schematron, but the current DocBook schema
-       uses RELAX NG for these definitions. Schematron can be used
-       only for validation, whereas grammar based schemas like
-       RELAX NG are useful also for other purposes like guided editing.</para>
-       </footnote></entry>
-       <entry>&no;</entry>
-       <entry>&no;</entry>
-       <entry>&no;</entry>
-       <entry>&yes;</entry>
-       <entry>&yes;</entry>
-      </row>
-      <row>
-       <entry>Hooks for MathML and SVG content</entry>
-       <entry>&no;</entry>
-       <entry>&no;</entry>
-       <entry>&no;</entry>
-       <entry>&yes;</entry>
-       <entry>&yes;</entry>
-      </row>
-      <row>
-       <entry>Link type integrity<footnote>
-       <para>Check whether ID/IDREF links are pointing to element
-       of corresponding type. For example that
-       <tag>footnoteref</tag> points to
-       <tag>footnote</tag>.</para></footnote></entry>
-       <entry>&no;</entry>
-       <entry>&no;</entry>
-       <entry>&yes;</entry>
-       <entry>&no;</entry>
-       <entry>&yes;</entry>
-      </row>
-      <row>
-       <entry>Presence of <tag dbk:class="attribute">version</tag>
-       attribute on the root element</entry>
-       <entry>&no;</entry>
-       <entry>&no;</entry>
-       <entry>&yes;</entry>
-       <entry>&no;</entry>
-       <entry>&yes;</entry>
-      </row>
-      <row>
-       <entry>Miscellaneous checks<footnote>
-       <para>For example consistency of segmented lists, only one
-       term inside term definition etc.</para></footnote></entry>
-       <entry>&no;</entry>
-       <entry>&no;</entry>
-       <entry>&yes;</entry>
-       <entry>&no;</entry>
-       <entry>&yes;</entry>
-      </row>
-      <row>
-       <entry>Element exclusions<footnote>
-       <para>Prevents improper nesting of elements, like admonition
-       inside admonition.</para></footnote></entry>
-       <entry>&no;</entry>
-       <entry>&no;</entry>
-       <entry>&yes;</entry>
-       <entry>&no;</entry>
-       <entry>&yes;</entry>
-      </row>        
-    </tbody>
-  </tgroup>
-</table>
-
-<section xml:id="schemas">
-<title>Where to get the schemas</title>
-
-<para>The latest versions of schemas can be obtained from <link
-xl:href="http://docbook.org/schemas/5x.html"/>. At the time this was
-written the latest version was &version;. Individual schemas are
-available at the following locations:</para>
-
-<variablelist>
-<varlistentry>
-<term>RELAX NG schema</term>
-<listitem><para><link xl:href="http://docbook.org/xml/&version;/rng/docbook.rng"/></para></listitem>
-</varlistentry>
-<varlistentry>
-<term>RELAX NG schema in compact syntax</term>
-<listitem><para><link xl:href="http://docbook.org/xml/&version;/rng/docbook.rnc"/></para></listitem>
-</varlistentry>
-<varlistentry>
-<term>DTD</term>
-<listitem><para><link xl:href="http://docbook.org/xml/&version;/dtd/docbook.dtd"/></para></listitem>
-</varlistentry>
-<varlistentry>
-<term>W3C XML Schema</term>
-<listitem><para><link xl:href="http://docbook.org/xml/&version;/xsd/docbook.xsd"/></para></listitem>
-</varlistentry>
-<varlistentry>
-<term>Schematron schema with additional checks</term>
-<listitem><para><link xl:href="http://docbook.org/xml/&version;/sch/docbook.sch"/></para></listitem>
-</varlistentry>
-</variablelist>
-
-<para>These schemas are also available from the mirror at
-<link xl:href="http://www.oasis-open.org/docbook/xml/&version;/"/>.</para>
-
-</section>
-
-<section xml:id="docs">
-<title>DocBook documentation</title>
-
-<para>Detailed documentation about each DocBook V5.0 element is
-presented in <link
-xl:href="http://docbook.org/tdg5/en/html/pt02.html">the reference part
-of <citetitle>DocBook: The Definitive Guide</citetitle></link>.</para>
-
-<note>
-<para>Other parts of <citetitle>DocBook: The Definitive
-Guide</citetitle> have not yet been updated to reflect the changes
-made in DocBook V5.0. Please do not be confused by this.</para>
-</note>
-
-</section>
-
-</section>
-
-</section>
-
-<section xml:id="tools">
-<title>Tool chain</title>
-
-<para>This section briefly describes tools and procedures to edit and
-process content stored in DocBook V5.0.</para>
-
-<section xml:id="editors">
-<title>Editing DocBook V5.0</title>
-
-<para>Because DocBook is an XML-based format and XML is a text-based
-format, you can use any text editor to create and edit DocBook V5.0
-documents. However, using <quote>dumb</quote> editors like Notepad is
-not very productive. You will do better if you use an editor that
-supports XML. Although there are DTD and W3C XML Schemas available for
-DocBook V5.0, which means you can use any editor that works with DTDs
-or W3C XML Schemas, we recommend that you use the RELAX NG grammar
-with DocBook V5.0. The rest of this section contains an overview of
-XML editors (listed in alphabetical order) that are known to work with
-RELAX NG schemas and that offer guided editing based on the RELAX NG
-schema.</para>
-
-<section xml:id="editors-nxml">
-<title>Emacs and nXML</title>
-
-<para><link xl:href="http://www.thaiopensource.com/nxml-mode/">nXML
-mode</link> is an add-on for the <application
-xl:href="http://www.gnu.org/software/emacs/emacs.html">GNU
-Emacs</application> text editor. By installing nXML you can turn Emacs
-into a very powerful XML editor that offers guided editing and
-validation of XML documents.</para>
-
-<figure xml:id="f.emacs">
-<title>Emacs with nXML mode provides guided editing and validation</title>
-<mediaobject>
-<imageobject dbk:role="html">
-<imagedata dbk:fileref="images/emacs.png"/>
-</imageobject>
-<imageobject dbk:role="fo">
-<imagedata dbk:fileref="images/emacs.png" dbk:width="100%"/>
-</imageobject>
-</mediaobject>
-</figure>
-
-<para>nXML uses a special configuration file named
-<filename>schemas.xml</filename> to associate schemas with XML
-documents. Often you will find this file in the directory
-<filename>site-lisp/nxml/schema</filename> inside the Emacs installation
-directory. Adding the following line into the configuration file,
-will associate DocBook V5.0 elements with the appropriate
-schema:</para>
-
-<programlisting>&lt;namespace ns="http://docbook.org/ns/docbook" uri="<replaceable>/path/to/</replaceable>docbook.rnc"/></programlisting>
-
-<note>
-<para>Please note that nXML ships with a file named
-<filename>docbook.rnc</filename>. This file contains the RELAX NG grammar
-for DocBook V4.x. Be sure that you associate the DocBook V5.0 namespace
-with the corresponding DocBook V5.0 grammar.</para>
-</note>
-
-<para>If you can't edit the global <filename>schemas.xml</filename> file,
-you can create this file in the same directory as your document. nXML will
-find associations placed there also. In this case you must create a
-complete configuration file like:</para>
-
-<programlisting>&lt;locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0">
-  &lt;namespace ns="http://docbook.org/ns/docbook" uri="<replaceable>/path/to/</replaceable>docbook.rnc"/>
-&lt;/locatingRules></programlisting>
-
-</section>
-
-<section xml:id="editors-oxygen">
-<title>oXygen</title>
-
-<para><application
-xl:href="http://www.oxygenxml.com/">oXygen</application> is a feature
-rich XML editor. It has built-in support for many schema languages
-including RELAX NG and it is preconfigured with many document types
-including DocBook. oXygen will assist you with writing DocBook V5.0
-content, and you will be able to validate your documents against both
-RELAX NG and Schematron schemas.</para>
-
-<figure xml:id="f.oxygen.open5">
-<title>DocBook V5.0 document opened in oXygen</title>
-<mediaobject>
-<imageobject>
-<imagedata dbk:fileref="images/oxygen4.png" dbk:width="100%"/>
-</imageobject>
-</mediaobject>
-</figure>
-
-<figure xml:id="f.oxygen.author.mode">
-<title>DocBook V5.0 document opened in oXygen in Author mode</title>
-<mediaobject>
-<imageobject>
-<imagedata dbk:fileref="images/oxygen5.png" dbk:width="100%"/>
-</imageobject>
-</mediaobject>
-</figure>
-
-</section>
-
-<section xml:id="editors-xxe">
-<title>XML Mind XML editor</title>
-
-<para><application xl:href="http://www.xmlmind.com/xmleditor/">XML
-Mind XML editor</application> (XXE) is a visual validating XML editor that
-provides a wordprocessor-like interface to users. It is available in
-two versions, Standard and Professional. The Standard version is free and
-provides everything you need to edit DocBook V5.0 documents.</para>
-
-<figure xml:id="f.xmlmind">
-<title>XML Mind XML Editor – feels almost like MS Word but real DocBook V5.0 markup is created</title>
-<mediaobject>
-<imageobject>
-<imagedata dbk:fileref="images/xxe.png" dbk:width="100%"/>
-</imageobject>
-</mediaobject>
-</figure>
-
-<para>In order to use DocBook V5.0 in XXE you have to install
-an add-on. Go to
-<menuchoice><guimenu>Options</guimenu><guimenuitem>Install
-Add-ons…</guimenuitem></menuchoice>. Then choose <guilabel>DocBook
-5 configuration</guilabel> and press the <guibutton>OK</guibutton>
-button. After restart, XXE is ready to work with DocBook V5.0
-documents.</para>
-
-</section>
-
-</section>
-
-<section xml:id="validators">
-<title>Validating DocBook V5.0</title>
-
-<para>If you are not using a RELAX NG-based validating editor when you
-create documents, we strongly recommend that you validate your
-documents against RELAX NG and Schematron schemas before processing
-them. Only after successful validation can you be sure that your
-document is really DocBook V5.0 and that processing tools will be able
-to process it correctly.</para>
-
-<para>For validation you can use tools that support simultaneous RELAX NG and
-Schematron validation, or you can use NVDL to orchestrate validation using
-the two schemas.</para>
-
-<section xml:id="validators-rng-sch">
-<title>Using RELAX NG and Schematron</title>
-
-<para>You can find a list of RELAX NG validators at <link
-xl:href="http://relaxng.org/#validators"/>. It is best to use
-validators with support for embedded Schematron rules inside RELAX NG
-schemas. Schematron is a rule-based validation language which is used
-to impose additional constraints on DocBook documents. Schematron rules
-assert conditions which are impossible or difficult to express 
-in a pure RELAX NG schema.</para>
-
-<para><application xl:href="https://msv.dev.java.net/">Sun 
-Multi-Schema XML Validator (MSV)</application> is able to validate an XML
-document against a RELAX NG schema and Schematron rules at the same time.
-To install and use MSV follow these steps:</para>
-
-<procedure>
-<step>
-<para>Download <filename>relames.zip</filename> from <link xl:href="https://msv.dev.java.net/servlets/ProjectDocumentList?folderID=101"/>.</para>
-</step>
-<step>
-<para>Unpack the downloaded file into an arbitrary directory.</para>
-</step>
-<step>
-<para>Validate your document using the following command:</para>
-<screen><command>java</command> -Xss512K -jar <replaceable>/path/to/</replaceable>relames.jar <replaceable>/path/to/</replaceable>docbook.rng document.xml</screen>
-<note>
-<para>The switch <option>-Xss512K</option> increases the stack size
-of the Java virtual machine. This is necessary because the DocBook schema is
-quite large. If you get stack overflow errors from MSV, increase
-this value. You may get spurious error messages if the value
-is too small, so if you get a stack overflow error, ignore any other error
-messages and try a larger value for the stack size.
-If you are not using Sun's Java implementation, please consult the
-documentation for your virtual machine to learn how to increase the stack
-size.</para>
-</note>
-</step>
-</procedure>
-
-<para>There is also an <link
-xl:href="http://relaxed.vse.cz/docbookvalidator/">on-line DocBook V5.0
-validator</link> that validates DocBook V5.0 documents against the normative
-RELAX NG schema with embedded Schematron rules.</para>
-
-</section>
-
-<section>
-<title>Using NVDL</title>
-
-<para>NVDL is a meta-schema language which can validate a document 
-against several schemas. DocBook V5.0 comes with a NVDL
-schema which specifies that DocBook documents should be validated
-against both RELAX NG and Schematron schemas.</para>
-
-<para>You can find a list of NVDL validators at <link
-xl:href="http://nvdl.org/"/>. The following procedures show how to
-install and use the <application
-xl:href="http://www.oxygenxml.com/onvdl.html">oNVDL</application> and
-<application xl:href="http://jnvdl.sourceforge.net">JNVDL</application>
-validators.</para>
-
-<procedure>
-<title>oNVDL installation and usage</title>
-<step>
-<para>Download <filename
-xl:href="http://www.oxygenxml.com/InstData/onvdl/onvdl-20070517.zip">onvdl-20070517.zip</filename>.</para>
-</step>
-<step>
-<para>Unpack the downloaded file into an arbitrary directory.</para>
-</step>
-<step>
-<para>Validate your document using the following command:</para>
-<screen><command>java</command> -jar <replaceable>/path/to/oNVDL/</replaceable>bin/onvdl.jar <replaceable>/path/to/</replaceable>docbook.nvdl document.xml</screen>
-</step>
-</procedure>
-
-<procedure>
-<title>JNVDL installation and usage</title>
-<step>
-<para>Download the latest release of JNVDL from <link
-xl:href="http://sourceforge.net/project/showfiles.php?group_id=164464"/>.</para>
-</step>
-<step>
-<para>Unpack the downloaded file into an arbitrary directory.</para>
-</step>
-<step>
-<para>Modify file <filename>jnvdl.bat</filename> (or <filename>jnvdl.sh</filename> on Unix based systems) to include <option>-Xss512K</option> switch directly after <command>java</command> command.</para>
-</step>
-<step>
-<para>On Windows systems, validate your document using the following command:</para>
-<screen><replaceable>/path/to/jnvdl/</replaceable><command>jnvdl</command> -nt -s <replaceable>/path/to/</replaceable>docbook.nvdl document.xml</screen>
-<para>On Unix systems, validate your document using the following command:</para>
-<screen><replaceable>/path/to/jnvdl/</replaceable><command>jnvdl.sh</command> -nt -s <replaceable>/path/to/</replaceable>docbook.nvdl document.xml</screen>
-</step>
-</procedure>
-
-</section>
-
-</section>
-
-<section xml:id="processing">
-<title>Processing DocBook V5.0</title>
-
-<para>Part of DocBook's great success can be attributed to the
-availability of free
-tools that can be used to transform DocBook content into various
-target formats including HTML and PDF. The DocBook XSL Stylesheets are
-very popular tools.</para>
-
-<section xml:id="dbxsl">
-<title>DocBook XSL Stylesheets</title>
-
-<para>The DocBook stylesheets are designed to process content written in
-different versions of DocBook (for example 3.1 and 4.2). Recent
-versions of the stylesheets are also able to process DocBook V5.0
-with some limitations.</para>
-
-<para>You can process DocBook V5.0 documents with the DocBook XSL
-stylesheets in exactly the same way you process DocBook V4.x documents.
-You do not need special software; you can stick to your preferred
-XSLT processor, be it Saxon, xsltproc, Xalan or whatever else (but see
-the note about the lost base URI below).</para>
-
-<para>During document processing, the stylesheets strip
-namespaces from DocBook V5.0 to get a document which will be
-very similar to DocBook V4.x. This is necessary because from the XSLT
-point of view, elements from different namespaces are distinct and cannot 
-be easily processed by the same set of templates. This process is
-completely transparent to the user. If you are processing DocBook V5.0
-documents, the only difference is that you will see the following
-additional message:</para>
-
-<screen>Note: namesp. cut : stripped namespace before processing
-Note: namesp. cut : processing stripped document</screen>
-
-<para>Although you can successfully use the existing stylesheets to
-process DocBook V5.0, there are some limitations and unsupported
-features. The unsupported features include:</para>
-
-<itemizedlist>
-<listitem><para>general annotations;</para></listitem>
-<listitem><para>general XLink links on all elements.</para></listitem>
-</itemizedlist>
-
-<note>
-<para>During namespace stripping, the base URI of the document is
-lost. This means that in rare situations, relatively referenced
-resources like images or programlistings can be processed incorrectly.
-The stylesheets attempt to compensate for this problem, but that is not always 
-possible. When an XSLT processor other than Saxon or Xalan is used, a warning 
-message is generated:
-
-<screen>WARNING: cannot add @xml:base to node set root element. Relative paths may not work.</screen>
-</para>
-
-</note>
-</section>
-
-<section xml:id="dbxsl-ns">
-<title>DocBook XSL-NS Stylesheets</title>
-<para>As you can see from reading the previous section, namespace
-  stripping has limitations that will cause trouble in some
-  situations. To overcome those limitations, Bob Stayton created a
-  build system for taking the non-namespace-aware DocBook XSL
-  stylesheets and generating namespace-aware versions from them.
-  The DocBook <link
-    xl:href="http://docbook.sourceforge.net/release/xsl-ns/current/"
-  >XSL-NS stylesheets</link> are the result.</para>
-
-<para>The DocBook XSL-NS stylesheets are released side-by-side
-  with the DocBook XSL stylesheets, as a separate <link
-  xl:href="https://sourceforge.net/project/showfiles.php?group_id=21935&amp;package_id=219178"
-  ><package>docbook-xsl-ns</package></link> package. They are the
-recommended XSLT 1.0 stylesheets to use for transforming
-namespaced (DocBook V5.0) documents.</para>
-</section>
-
-<section xml:id="dbxsl2">
-<title>XSLT 2.0 based re-implementation</title>
-
-<para>XSLT 1.0 is missing some important features. To work around
-these missing features, the current DocBook XSL stylesheets use some
-implementation-specific extensions.
-XSLT 2.0 adds many new and previously missing features into the language.
-A new set of DocBook stylesheets is being implemented based on XSLT 2.0
-to take advantage of these features and to fully support DocBook V5.0.
-</para>
-
-<para>The XSLT 2.0 based stylesheets have many new features, including:</para>
-
-<itemizedlist>
-<listitem><para>seamless integration of profiling (conditional
-documents) with external bibliographies and
-glossaries;</para></listitem>
-<listitem><para>no need for (most) external extensions;</para></listitem>
-<listitem><para>internationalized indexes;</para></listitem>
-<listitem><para>easy to customize titlepage templates.</para></listitem>
-</itemizedlist>
-
-<para>The XSLT 2.0 based stylesheets are still under development.  At
-this writing, they only support HTML and chunked HTML output.  As time
-permits, the stylesheet developers will be adding other formats.  Since
-the stylesheets are developed in the limited free time the developers
-have, there's no specific schedule.</para>
-
-<para>There are not very many XSLT 2.0 implementations available.
-But, if you want to try the new stylesheets, grab a snapshot of
-the development version from <link
-xl:href="http://docbook.sourceforge.net/snapshots/docbook-xsl2-snapshot.zip"/>
-and unpack it somewhere. Then download and install Saxon 9 from <link
-xl:href="http://saxon.sf.net"/>.</para>
-
-<para>To transform a DocBook V5.0 document to a single HTML page use the command:</para>
-
-<screen><command>java</command> -jar <replaceable>/path/to/</replaceable>saxon9.jar -o output.html document.xml <replaceable>/path/to/</replaceable>docbook-xsl2-snapshot/html/docbook.xsl</screen>
-
-<para>To transform a DocBook V5.0 document to a set of chunked HTML pages use the command:</para>
-
-<screen><command>java</command> -jar <replaceable>/path/to/</replaceable>saxon9.jar document.xml <replaceable>/path/to/</replaceable>docbook-xsl2-snapshot/html/chunk.xsl</screen>
-
-</section>
-
-</section>
-
-</section>
-
-<section xml:id="changes">
-<title>Markup changes</title>
-
-<para>This section describes the most common markup changes
-between DocBook V4.x and V5.0.
-You can find a complete list of changes in
-<citation>DB5SPEC</citation>.</para>
-
-<section xml:id="changes-linking">
-<title>Improved cross-referencing and linking</title>
-
-<para>In DocBook V4.x the attribute <tag dbk:class="attribute">id</tag> is
-used to assign a unique identifier to an element. In DocBook V5.0 this
-attribute is renamed <tag dbk:class="attribute">xml:id</tag> in order
-to comply with <citation>XMLID</citation>.</para>
-
-<para>Now you can use almost any inline element as the source of a link,
-not just <tag>xref</tag> or <tag>link</tag>. For example, the following
-DocBook 4.x content:</para> 
-
-<programlisting><![CDATA[<section id="dir">
-  <title>DIR command</title>
-  <para>...</para>
-</section>
-
-<section id="ls">
-  <title>LS command</title>
-  <para>This command is a synonym for <link linkend="dir"><command>DIR</command></link> command.</para>
-</section>]]></programlisting>
-
-<para>is written in DocBook V5.0 as:</para>
-
-<programlisting><![CDATA[<section xml:id="dir">
-  <title>DIR command</title>
-  <para>...</para>
-</section>
-
-<section xml:id="ls">
-  <title>LS command</title>
-  <para>This command is a synonym for <command linkend="dir">DIR</command> command.</para>
-</section>]]></programlisting>
-
-<para>The <tag dbk:class="attribute">linkend</tag> attribute was added to all
-inline elements together with the <tag dbk:class="attribute">href</tag>
-attribute from the XLink namespace. This means that you can use any inline
-element as the source of a hypertext link. To use XLinks you have
-to declare the XLink namespace (most often on the root element of your
-document):</para>
-
-<programlisting><![CDATA[<article xmlns="http://docbook.org/ns/docbook" 
-         xmlns:xl="http://www.w3.org/1999/xlink" version="5.0">
-  <title>Test article</title>
-
-  <para><application xl:href="http://www.gnu.org/software/emacs/emacs.html">Emacs</application> 
-    is my favourite text editor.</para>]]>
-  …</programlisting>
-
-<para>The <tag dbk:condition="v4">ulink</tag> element was removed from DocBook V5.0
-in favor of XLink linking. Instead of the DocBook V4.x <tag dbk:condition="v4">ulink</tag>
-element:</para>
-
-<programlisting><![CDATA[<ulink url="http://docbook.org">DocBook site</ulink>]]></programlisting>
-
-<para>you can now use <tag>link</tag></para>
-
-<programlisting><![CDATA[<link xl:href="http://docbook.org">DocBook site</link>]]></programlisting>
-
-<para>XLink links may contain a fragment identifier, which you can 
-use instead of <tag dbk:class="attribute">linkend</tag> to form
-cross-references inside a document; for example:</para>
-
-<programlisting><![CDATA[<command xl:href="#dir">DIR</command>]]></programlisting>
-
-<para>However XLink links are not checked during validation, while <tag
-dbk:class="attribute">xml:id</tag>/<tag dbk:class="attribute">linkend</tag>
-links are checked for ID/IDREF consistency.
-One place where the XLink-based, fragment identifier scheme is
-useful is when XInclude is being used, since XML ID/IDREF links
-cannot span XInclude boundaries.
-You can use whichever approach better suits your needs.</para>
-</section>
-
-<section xml:id="changes-renamed">
-<title>Renamed elements</title>
-
-<para>Some elements were renamed to better express their meaning or to
-reduce the total number of elements available in DocBook.</para>
-
-<table xml:id="t.renamed">
-<title>Renamed elements</title>
-<tgroup dbk:cols="2">
-<thead>
-<row>
-<entry>Old name</entry>
-<entry>New name</entry>
-</row>
-</thead>
-<tbody>
-<row>
-<entry><tag dbk:condition="v4">sgmltag</tag></entry>
-<entry><tag>tag</tag></entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">bookinfo</tag>, <tag dbk:condition="v4">articleinfo</tag>,
-<tag dbk:condition="v4">chapterinfo</tag>, <tag dbk:condition="nolink">*info</tag></entry>
-<entry><tag>info</tag></entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">authorblurb</tag></entry>
-<entry><tag>personblurb</tag></entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">collabname</tag>, <tag dbk:condition="v4">corpauthor</tag>,
-<tag dbk:condition="v4">corpcredit</tag>, <tag dbk:condition="v4">corpname</tag></entry>
-<entry><tag>orgname</tag></entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">isbn</tag>, <tag dbk:condition="v4">issn</tag>,
-<tag dbk:condition="v4">pubsnumber</tag></entry>
-<entry><tag>biblioid</tag></entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">lot</tag>, <tag dbk:condition="v4">lotentry</tag>, <tag dbk:condition="v4">tocback</tag>,
-<tag dbk:condition="v4">tocchap</tag>, <tag dbk:condition="v4">tocfront</tag>, <tag dbk:condition="v4">toclevel1</tag>,
-<tag dbk:condition="v4">toclevel2</tag>, <tag dbk:condition="v4">toclevel3</tag>, <tag dbk:condition="v4">toclevel4</tag>,
-<tag dbk:condition="v4">toclevel5</tag>, <tag dbk:condition="v4">tocpart</tag></entry>
-<entry><tag>tocdiv</tag></entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">graphic</tag>, <tag dbk:condition="v4">graphicco</tag>,
-<tag dbk:condition="v4">inlinegraphic</tag>, <tag dbk:condition="v4">mediaobjectco</tag></entry>
-<entry><tag>mediaobject</tag> and <tag>inlinemediaobject</tag></entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">ulink</tag></entry>
-<entry><tag>link</tag></entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">ackno</tag></entry>
-<entry><tag>acknowledgements</tag></entry>
-</row>
-</tbody>
-</tgroup>
-</table>
-
-</section>
-
-<section xml:id="changes-removed">
-<title>Removed elements</title>
-
-<para>The following elements were removed from DocBook V5.0 without
-direct replacements: <tag dbk:condition="v4">action</tag>, <tag
-dbk:condition="v4">beginpage</tag>, <tag dbk:condition="v4">highlights</tag>,
-<tag dbk:condition="v4">interface</tag>, <tag
-dbk:condition="v4">invpartnumber</tag>, <tag
-dbk:condition="v4">medialabel</tag>, <tag dbk:condition="v4">modespec</tag>,
-<tag dbk:condition="v4">structfield</tag>, <tag
-dbk:condition="v4">structname</tag>.
-If you use one or more of these elements, here are some suggestions
-as to how to re-code them in DocBook V5.0.
-</para>
-
-<table xml:id="t.removed">
-<title>Recommended mapping for removed elements</title>
-<tgroup dbk:cols="2">
-<thead>
-<row>
-<entry>Old name</entry>
-<entry>Recommended mapping</entry>
-</row>
-</thead>
-<tbody>
-<row>
-<entry><tag dbk:condition="v4">action</tag></entry>
-<entry>Use <computeroutput>&lt;<tag>phrase</tag> remap="action"&gt;</computeroutput>.</entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">beginpage</tag></entry>
-<entry>Remove: <tag dbk:condition="v4">beginpage</tag> is advisory only
-and has tended to cause confusion.  A processing instruction or
-comment should be a workable replacement if one is needed.</entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">highlights</tag></entry>
-<entry>Use <tag>abstract</tag>.  Note that because <tag
-dbk:condition="v4">highlights</tag> has a broader content model, you may
-need to wrap contents in a <tag>para</tag> inside
-<tag>abstract</tag>.</entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">interface</tag></entry>
-<entry>Use one of the <quote>gui*</quote> elements
-(<tag>guibutton</tag>, <tag>guiicon</tag>, <tag>guilabel</tag>,
-<tag>guimenu</tag>, <tag>guimenuitem</tag>, or
-<tag>guisubmenu</tag>).</entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">invpartnumber</tag></entry>
-<entry>Use <computeroutput>&lt;<tag>biblioid</tag> class="other"
-otherclass="medialabel"&gt;</computeroutput>.  The
-<tag>productnumber</tag> element is another alternative.</entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">medialabel</tag></entry>
-<entry>Use <computeroutput>&lt;<tag>citetitle</tag>
-pubwork="<replaceable>mediatype</replaceable>"&gt;</computeroutput>,
-where <replaceable>mediatype</replaceable> is the type of media being
-labeled (e.g.,<tag dbk:class="attvalue">cdrom</tag> or <tag
-dbk:class="attvalue">dvd</tag>).</entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">modespec</tag></entry>
-<entry>No longer needed.  The current processing model for
-<tag>olink</tag> renders <tag dbk:condition="v4">modespec</tag>
-unnecessary.</entry>
-</row>
-<row>
-<entry><tag dbk:condition="v4">structfield</tag>, <tag dbk:condition="v4">structname</tag></entry>
-<entry>Use <tag>varname</tag>. If you need to distinguish between the
-two, use <computeroutput>&lt;<tag>varname</tag>
-remap="<replaceable>structname or
-structfield</replaceable>"&gt;</computeroutput>.  In some contexts, it
-may also be appropriate to use <tag>property</tag> for <tag
-dbk:condition="v4">structfield</tag>.</entry>
-</row>
-</tbody>
-</tgroup>
-</table>
-
-</section>
-
-</section>
-
-<section xml:id="convert4to5">
-<title>Converting DocBook V4.x documents to DocBook V5.0</title>
-
-<para>The DocBook V5.0 schema ships with an XSLT 1.0 stylesheet that
-is designed to transform valid DocBook V4.x documents to valid
-DocBook V5.0 documents.</para>
-
-<para>To convert your document, <filename>doc.xml</filename> in the
-examples below, follow these steps:</para>
-
-<procedure>
-<step>
-<para>Check the validity of your DocBook XML V4.x document. The
-conversion tool assumes that the input document is valid. If the input
-document contains markup errors, the results will be unpredictable at
-best.</para>
-</step>
-<step>
-<para>Transform <filename>doc.xml</filename> to
-<filename>newdoc.xml</filename> with the
-<filename>db4-upgrade.xsl</filename> stylesheet included in the
-DocBook V5.0 distribution that you are using.</para>
-</step>
-<step>
-<para>Check the validity of your DocBook XML V5.0 document against
-the DocBook V5.0 RELAX NG grammar.</para>
-</step>
-</procedure>
-
-<para>In the vast majority of cases, the resulting document should
-be valid and your conversion process is finished.</para>
-
-<para>If the document is not valid, please report the problem.
-(Over time, we'll have more experience with the sorts of things
-that can go wrong and we'll update this document to reflect that
-experience.)</para>
-
-<section xml:id="entities">
-<title>What About Entities?</title>
-
-<para>Using XSLT to transform existing documents to DocBook V5.0 has
-one potential disadvantage: it removes all entity references from 
-your document.</para>
-
-<para>If preserving entities is an important aspect of your production
-work flow, you will have to engage in a semi-manual process to
-preserve them.</para>
-
-<procedure>
-<step>
-<para>Open your existing document using your favorite editing tool.
-You must use a tool that <emphasis>is not</emphasis> XML-aware, or one
-that allows you to edit markup “in the raw”.</para>
-</step>
-<step>
-<para>Replace all occurrences of the entity references that you want
-to preserve with some unique string. For example, if you want to preserve
-“<literal>&amp;Product;</literal>” references, you could replace them
-all with “<literal>[[[Product]]]</literal>” (assuming that the string
-“<literal>[[[Product]]]</literal>” doesn't occur anywhere else in your document).</para>
-</step>
-<step>
-<para>Copy the document type declaration off of your document and save
-it some place. The document type declaration is everything from
-“<literal>&lt;!DOCTYPE</literal>” to the closing “<literal>]></literal>”.
-</para>
-</step>
-<step>
-<para>Perform the conversion described in <xref dbk:linkend="convert4to5"/>.
-</para>
-</step>
-<step>
-<para>Open the new document using your favorite editing tool. Replace
-all occurrences of the unique string you used to save the entity references
-with the corresponding entity references.</para>
-</step>
-<step>
-<para>Paste the document type declaration that you saved onto the top
-of your new document.</para>
-</step>
-<step>
-<para>Remove the external identifier (the <literal>PUBLIC</literal>
-and/or <literal>SYSTEM</literal> keywords) from the document type
-declaration. A document that begins:</para>
-<programlisting><![CDATA[<!DOCTYPE book [
-<!ENTITY someEntity "some replacement text">
-]>]]></programlisting>
-<para>is perfectly well-formed. If you don't remove the references to
-the DTD, then your parser will likely try to validate against DocBook
-V4.0 and that's not going to work. Alternatively, you could refer
-to the DocBook V5.0 DTD.</para>
-</step>
-</procedure>
-
-<tip>
-<para>Steps 2 and 5 from previous procedure can be automated using the
-<link xl:href="http://docbook.svn.sourceforge.net/viewvc/docbook/trunk/contrib/tools/cloak">cloak
-script</link> written by Michael Smith.</para>
-</tip>
-
-<section xml:id="extparsedentities">
-<title>External Parsed Entities</title>
-
-<para>External parsed entities, entities which load part of a document
-from another file, are a special case. These can often be replaced
-with XInclude elements.</para>
-
-<para>The Perl script <filename>db4-entities.pl</filename>, also included
-in the DocBook V5.0 distribution attempts to perform this replacement
-for you. To use the script, perform the following steps:</para>
-
-<procedure>
-<step>
-<para>Process your document with <filename>db4-entities.pl</filename>.
-The script expects a single filename and prints the XInclude version
-on standard output.</para>
-</step>
-<step>
-<para>Process the XInclude version as described in <xref
-dbk:linkend="convert4to5"/>.
-</para>
-</step>
-</procedure>
-</section>
-</section>
-
-</section>
-
-<section xml:id="customizations">
-  <title>Customizing DocBook V5.0</title>
-  <!--
-      ** RNG schema organization
-      ** Removing attributes
-      ** Adding new attributes
-      ** Changing permitted content of attribute
-      ** Removing elements
-      ** Adding new elements
-      ** Customizing content models
-      ** Naming and versioning of DocBook customizations
-  -->
-
-  <para>
-    It's much easier to customize DocBook V5.0 than it was to
-    customize earlier releases.  This is partly because RELAX NG
-    provides better support for modifications than DTDs and partly
-    because the DocBook schema is designed to take full advantage
-    of the capabilities RELAX NG provides.
-    This section describes the organization of the RELAX NG schema for
-    DocBook, methods and examples for adding, removing, and modifying elements
-    and attributes, and conventions for naming and versioning
-    DocBook customizations.
-    It assumes some familiarity with RELAX NG.  If you are unfamiliar
-    with RELAX NG, you can find a tutorial introduction in
-    <citation>RNCTUT</citation>.
-  </para>
-  <section xml:id="relaxngorg">
-    <title>DocBook RELAX NG schema organization</title>
-    <para>
-      The DocBook RELAX NG schema is highly modular, using named
-      patterns extensively.  Every element, attribute, attribute
-      list, and enumeration has its own named pattern.  In addition,
-      there are named patterns for logical combinations of elements
-      and attributes.  These named patterns provide <quote>hooks</quote>
-      into the schema that allow you to do a wide range of customization
-      by simply redefining one or more of the named patterns.
-    </para>
-    <para>
-      An important design characteristic of the schema is that
-      duplication is minimized.  This is done through the use of
-      named patterns for common groupings that can be re-used.
-      For example, the <tag>imagedata</tag> and <tag>videodata</tag>
-      elements each have an <tag dbk:class="attribute">align</tag> attribute
-      that takes the same set of enumerated values.  Rather than
-      repeating those values, a single pattern,
-      <varname>db.halign.enumeration</varname> is referenced by
-      the <varname>db.videodata.align.enumeration</varname>
-      and <varname>db.imagedata.align.enumeration</varname> patterns,
-      which are in turn referenced by the
-      <varname>db.videodata.align.attribute</varname>
-      and <varname>db.imagedata.align.attribute</varname> patterns.
-      While this may seem like overkill, it allows a customizer to modify
-      the allowed enumerations for these two attributes separately or together,
-      or to completely re-define the allowed content of either or both,
-      by redefining one or more of these named patterns.
-    </para>
-    <section xml:id="patternnames"><title>Pattern Names</title>
-    <para>
-      Because named patterns are used extensively, the RELAX NG schema uses
-      several naming conventions.  These are:
-      <itemizedlist dbk:spacing="compact">
-        <listitem>
-          <para>
-            Names have two or more parts, separated by dots <quote>.</quote>
-          </para>
-        </listitem>
-        <listitem>
-          <para>
-            The first part of each name is the prefix <quote>db</quote>
-          </para>
-        </listitem>
-        <listitem>
-          <para>
-            Each element has a named pattern in the form
-            <varname>db.<replaceable>elementname</replaceable></varname>.
-            Elements that have different content models in different
-            contexts will also have patterns in the form
-            <varname>db.<replaceable>context.elementname</replaceable></varname>.  For example, <varname>db.figure.info</varname>
-            defines the content model for the <tag>info</tag> element
-            when it appears as a child of the <tag>figure</tag> element.
-            <replaceable>Context</replaceable> may have several parts.
-            For example, <varname>db.cals.entrytbl.thead</varname>.
-          </para>
-        </listitem>
-        <listitem>
-          <para>
-            Most attributes have a named pattern in the form
-            <varname>db.<replaceable>attributename</replaceable>.attribute</varname>.
-            Attributes that have different content models in different
-            contexts will also have patterns in the form
-            <varname>db.<replaceable>context.attributename</replaceable>.attribute</varname>.
-            For example,
-            <varname>db.olink.localinfo.attribute</varname> defines the content
-            model of the <tag dbk:class="attribute">localinfo</tag> attribute when
-            it appears in <tag>olink</tag>.
-            There are a few attributes that do not have individual named
-            patterns.  For example, the effectivity attributes are grouped
-            into <varname>db.effectivity.attributes</varname> and not identified
-            separately.
-          </para>
-        </listitem>
-        <listitem>
-          <para>
-            Each element has a named pattern for its attribute list in
-            the form
-            <varname>db.<replaceable>elementname</replaceable>.attlist</varname>
-
-            that defines the list of attributes for that element.
-            Elements that have different attribute lists in different
-            contexts will also have patterns in the form
-            <varname>db.<replaceable>context.elementname</replaceable>.attlist</varname>
-            For example, <varname>db.html.table.attlist</varname> defines
-            the attribute list for the html <tag dbk:condition="nolink">table</tag> element and
-            <varname>db.cals.table.attlist</varname> defines the attribute
-            list for a cals <tag dbk:condition="nolink">table</tag> element.
-          </para>
-        </listitem>
-        <listitem>
-          <para>
-            Each attribute that has enumerated values has a
-            named pattern in the form
-            <varname>db.<replaceable>[context.]attributename</replaceable>.enumeration</varname>.
-            If the enumeration for a particular attribute depends on
-            context, optional context is provided.
-            For example,
-            <varname>db.verbatim.continuation.enumeration</varname> defines
-            the enumeration values for the
-            <tag dbk:class="attribute">continuation</tag> attribute that is used
-            in verbatim contexts like <tag>screen</tag>.
-            Unlike elements and attributes, there is not necessarily a
-            named pattern for enumerated attributes outside their context.
-            For example, there is no <varname>db.class.enumeration</varname>
-            because the <tag dbk:class="attribute">class</tag> attribute has
-            a broad and non-intersecting range of uses.
-          </para>
-        </listitem>
-        <listitem>
-          <para>
-            There are several different groupings of elements and attributes.
-            Here are the major ones:
-            <variablelist dbk:spacing="compact">
-              <varlistentry>
-                <term>inlines</term>
-                <listitem>
-                  <para>
-                    Combinations of inline elements, for example,
-                    <varname>db.error.inlines</varname>, which contains
-                    <varname>db.errorcode</varname>,
-                    <varname>db.errortext</varname>, etc.
-                  </para>
-                </listitem>
-              </varlistentry>
-              <varlistentry>
-                <term>blocks</term>
-                <listitem>
-                  <para>
-                    Combinations of block elements, for example,
-                    <varname>db.verbatim.blocks</varname>, which contains
-                    <varname>db.programlisting</varname>,
-                    <varname>db.screen</varname>, etc.
-                  </para>
-                </listitem>
-              </varlistentry>
-              <varlistentry>
-                <term>attributes</term>
-                <listitem>
-                  <para>
-                    Combinations of attributes, for example,
-                    <varname>db.effectivity.attributes</varname>,
-                    which contains the attributes
-                    <tag dbk:class="attribute">arch</tag>,
-                    <tag dbk:class="attribute">condition</tag>,
-                    <tag dbk:class="attribute">conformance</tag>, etc.
-                  </para>
-                </listitem>
-              </varlistentry>
-              <varlistentry>
-                <term>components</term>
-                <listitem>
-                  <para>
-                    High level components of the schema, for example,
-                    <varname>db.navigation.components</varname>, which contains
-                    <varname>db.glossary</varname>,
-                    <varname>db.bibliography</varname>,
-                    <varname>db.index</varname>, and
-                    <varname>db.toc</varname>, and is used inside the
-                    content model for <tag>chapter</tag>, <tag>appendix</tag>,
-                    and <tag>preface</tag>.
-                  </para>
-                </listitem>
-              </varlistentry>
-              <varlistentry>
-                <term>contentmodel</term>
-                <listitem>
-                  <para>
-                    Shared content models, for example,
-                    <varname>db.admonition.contentmodel</varname>, which contains
-                    the content model for <tag>tip</tag>, <tag>warning</tag>,
-                    <tag>note</tag>, etc.
-                  </para>
-                </listitem>
-              </varlistentry>
-            </variablelist>
-          </para>
-          <para>
-            There are a couple of other groupings designed to minimize
-            duplication, but these are the most important.
-          </para>
-        </listitem>
-      </itemizedlist>
-    </para>
-  </section>
-</section>
-<section xml:id="customconsiderations">
-  <title>General customization considerations</title>
-  <para>
-    Creating a customized schema is similar to
-    creating a customization layer for XSL.  The schema customization
-    layer is a new RELAX NG schema that defines your changes and
-    includes the standard docbook schema.  You then validate using
-    the schema customization as your schema.
-  </para>
-  <para>
-    <xref dbk:linkend="ex-empty" dbk:xrefstyle="select: label"/> is an empty
-    RELAX NG customization that does nothing
-    except define the name spaces and include the standard DocBook schema.
-    The <tag dbk:class="attribute">href</tag> attribute of the
-    <tag dbk:condition="nolink">include</tag> element points to
-    the location of the standard DocBook V5.0
-    schema.<footnote><para>The examples in this section use
-    <filename>docbook.rng</filename> as the schema location. If you want
-    to create a portable schema customization you should use a standard
-    web-accessible location like
-    <uri>http://docbook.org/xml/&version;/rng/docbook.rng</uri> and
-    then use <link
-    xl:href="http://www.oasis-open.org/committees/download.php/14809/xml-catalogs.html">XML
-    catalogs</link> to resolve this location to your local copy of the
-    schema for improved performance. Unfortunately, at the time of
-    this writing not all RELAX NG validators support XML catalogs.</para></footnote>
-    All of the examples are given in both RNG and RNC form.
-<example xml:id="ex-empty"><title>Empty customization file</title>
-<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns:db="http://docbook.org/ns/docbook"
-         ns="http://docbook.org/ns/docbook"
-         xmlns="http://relaxng.org/ns/structure/1.0">
-  <include href="docbook.rng"/>
-
-  <!-- redefinitions of named patterns -->
-
-</grammar>]]></programlisting>
-<programlisting dbk:language="rnc"><![CDATA[namespace db = "http://docbook.org/ns/docbook"
-
-include "docbook.rnc" inherit = db
-# redefinitions of named patterns]]></programlisting>
-</example>
-  </para>
-</section>
-  <section xml:id="cust-elements">
-    <title>Elements</title>
-    <section xml:id="cust-add-elements">
-      <title>Adding elements</title>
-      <para>
-        Adding an element typically takes two definitions.
-        The first defines the new element and
-        its content model, and the second adds the
-        new element into the schema.  We'll show two examples.
-      </para>
-      <para>
-        <xref dbk:linkend="ex-add-element-1"  dbk:xrefstyle="select: label"/>
-        adds a new element,
-        <tag dbk:condition="nolink">person</tag>, with the same
-        content model as <tag>author</tag>.  The new element will be
-        allowed to appear wherever <tag>author</tag> can appear.
-      </para>
-      <para>
-        The <varname>db.author</varname> pattern is copied
-        and renamed <varname>dbx.person</varname>, defining
-        a new element called <tag dbk:condition="nolink">person</tag>.
-        Then, the <varname>db.author</varname> pattern is redefined
-        to be a choice of the current value or <varname>dbx.person</varname>.
-        The <tag dbk:class="attribute">combine</tag> attribute tells
-        RELAX NG to combine this pattern with the existing named
-        pattern.  In this case, the value
-        of the <tag dbk:class="attribute">combine</tag> attribute is
-        <quote>choice</quote>, which tells the parser that either
-        the original pattern or this new pattern is a valid match.
-      </para>
-<example xml:id="ex-add-element-1"><title>Adding a new element by duplicating an existing one</title>
-<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns:db="http://docbook.org/ns/docbook"
-         ns="http://docbook.org/ns/docbook"
-         xmlns="http://relaxng.org/ns/structure/1.0">
-  <include href="docbook.rng"/>
-  <!-- define the new element -->
-  <define name="dbx.person">
-    <element name="person">
-        <ref name="db.author.attlist"/>
-        <ref name="db.credit.contentmodel"/>
-    </element>
-  </define>
-  <!-- redefine the db.author pattern to allow db.person in
-       the same places as db.author -->
-  <define name="db.author" combine="choice">
-    <ref name="dbx.person"/>
-  </define>
-</grammar>]]></programlisting>
-<programlisting dbk:language="rnc"><![CDATA[default namespace db = "http://docbook.org/ns/docbook"
-
-include "docbook.rnc"
-# define the new element
-dbx.person =
-  element person { db.author.attlist, db.credit.contentmodel }
-# redefine the db.author pattern to allow db.person in
-# the same places as db.author
-db.author |= dbx.person]]></programlisting>
-</example>
-    <para>
-      The preceding method works well when you'd like a new element
-      to be a clone or near-clone of an existing element.  It gives
-      you complete control over the content model, but
-      only limited control over where the element is allowed.  It
-      works well when you want to allow the element in the same places
-      as an existing element, and for this example that works
-      nicely, since <tag>author</tag> is allowed in four different
-      named patterns, each of which would have had to be redefined to
-      allow <tag dbk:condition="nolink">person</tag>.
-      But, if you can't find an existing element that is allowed in
-      exactly the places you need, this method doesn't work as well.
-    </para>
-    <para>
-      <xref dbk:linkend="ex-add-element-2" dbk:xrefstyle="select: label"/>
-      adds two new elements by combining them into
-      a higher level pattern.  In this example, we'll add
-      two new inline elements for writing about assembly language,
-      <tag dbk:condition="nolink">register</tag> and 
-      <tag dbk:condition="nolink">instruction</tag>.
-      We will allow them wherever programming inlines
-      or operating system inlines are allowed.
-      <xref dbk:linkend="ex-add-element-2" dbk:xrefstyle="select: label"/>
-      defines the two elements, creates a new named pattern
-      (<varname>dbx.asm.inlines</varname>) that contains them, and adds
-      that pattern to <varname>db.programming.inlines</varname> and
-      <varname>db.os.inlines</varname>.  Since these two patterns
-      don't have any elements in common, the strategy used in 
-      <xref dbk:linkend="ex-add-element-1" dbk:xrefstyle="select: label"/>
-      would require selecting two different elements to <quote>clone</quote>,
-      which would be messy.
-    </para>
-<example xml:id="ex-add-element-2"><title>Adding new inline elements</title>
-<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns:db="http://docbook.org/ns/docbook"
-         ns="http://docbook.org/ns/docbook"
-         xmlns="http://relaxng.org/ns/structure/1.0">
-  <include href="docbook.rng"/>
-  <!-- define the new elements -->
-  <define name="dbx.register">
-    <element name="register">
-      <text/>
-    </element>
-  </define>
-  <define name="dbx.instruction">
-    <element name="instruction">
-      <text/>
-    </element>
-  </define>
-  <!-- create a new pattern that contains the new inlines -->
-  <define name="dbx.asm.inlines">
-    <choice>
-      <ref name="dbx.register"/>
-      <ref name="dbx.instruction"/>
-    </choice>
-  </define>
-  <!-- add the new inlines to programming and os inlines -->
-    <define name="db.programming.inlines" combine="choice">
-      <ref name="dbx.asm.inlines"/>
-    </define>
-    <define name="db.os.inlines" combine="choice">
-      <ref name="dbx.asm.inlines"/>
-    </define>
-</grammar>]]></programlisting>
-<programlisting dbk:language="rnc"><![CDATA[default namespace db = "http://docbook.org/ns/docbook"
-
-include "docbook.rnc"
-# define the new elements
-dbx.register = element register { text }
-dbx.instruction = element instruction { text }
-# create a new pattern that contains the new inlines
-dbx.asm.inlines = dbx.register | dbx.instruction
-# add the new inlines to programming and os inlines
-db.programming.inlines |= dbx.asm.inlines
-db.os.inlines |= dbx.asm.inlines]]></programlisting>
-</example>
-    </section>
-    <section xml:id="cust-delete-elements">
-      <title>Deleting elements</title>
-      <para>
-        Deleting elements is straightforward, but takes some
-        care and planning.  <xref dbk:linkend="ex-delete-element"
-        dbk:xrefstyle="select: label"/> deletes
-        the <tag>important</tag> admonition element by redefining
-        it with a content model of <varname>notAllowed</varname>.
-        Note that in this example, the redefinition is inside
-        the <tag dbk:condition="nolink">include</tag> element.
-        This is required for
-        redefinitions that completely replace an existing pattern.
-      </para>
-      <para>
-        Be careful; If you delete an element that is a required part
-        of another element's content model, you can make it
-        impossible to create a valid document.
-        For example, if you delete the <tag>title</tag>
-        element, you won't be able to validate a <tag>book</tag>
-        because a <tag>book</tag> requires a <tag>title</tag>.
-      </para>
-<example xml:id="ex-delete-element"><title>Deleting an element</title>
-<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns:db="http://docbook.org/ns/docbook"
-         ns="http://docbook.org/ns/docbook"
-         xmlns="http://relaxng.org/ns/structure/1.0">
-  <include href="docbook.rng">
-    <!-- redefine important element as notAllowed -->
-    <define name="db.important">
-      <notAllowed/>
-    </define>
-  </include>
-</grammar>]]></programlisting>
-<programlisting dbk:language="rnc"><![CDATA[namespace db = "http://docbook.org/ns/docbook"
-
-include "docbook.rnc" inherit = db {
-  # redefine important element as notAllowed
-  db.important = notAllowed
-}]]></programlisting>
-</example>
-    </section>
-    <section xml:id="cust-modify-elements">
-      <title>Customizing the content model of existing elements</title>
-      <para>
-         <xref dbk:linkend="ex-modify-element" dbk:xrefstyle="select: label"/>
-         expands the definition of <tag>author</tag> to include two
-         new elements, <tag dbk:condition="nolink">born</tag> and
-         <tag dbk:condition="nolink">died</tag>.
-         The <tag>author</tag> element allows two content models,
-         <varname>db.person.author.contentmodel</varname>, which
-         defines an author who is a person, and
-         <varname>db.org.author.contentmodel</varname>, which
-         defines an author that is an organization.  We will modify
-         <varname>db.person.author.contentmodel</varname> so that
-         only authors who are persons can have the new elements.
-<example xml:id="ex-modify-element"><title>Modifying the content model of an element</title>
-<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns:db="http://docbook.org/ns/docbook"
-         ns="http://docbook.org/ns/docbook"
-         xmlns="http://relaxng.org/ns/structure/1.0">
-  <include href="docbook.rng"/>
-
-  <define name="db.person.author.contentmodel" combine="interleave">
-    <interleave>
-      <optional>
-        <element name="born">
-          <ref name="db.date.contentmodel"/>
-        </element>
-      </optional>
-      <optional>
-        <element name="died">
-          <ref name="db.date.contentmodel"/>
-        </element>
-      </optional>
-    </interleave>
-  </define>
-</grammar>]]></programlisting>
-<programlisting dbk:language="rnc"><![CDATA[default namespace = "http://docbook.org/ns/docbook"
-namespace db = "http://docbook.org/ns/docbook"
-
-include "docbook.rnc"
-
-db.person.author.contentmodel &=
-  element born { db.date.contentmodel }?
-  & element died { db.date.contentmodel }?]]></programlisting>
-</example>
-      </para>
-      <para>
-        This modification will allow instances like this:
-<programlisting><![CDATA[<author>
-  <personname>Babe Ruth</personname>
-  <born>02/06/1895</born>
-  <died>08/16/1948</died>
-</author>]]></programlisting>
-but because we only modified the content model for authors
-who are human, it won't allow an instance like this, which
-uses <varname>db.org.author.contentmodel</varname>:
-<programlisting><![CDATA[<!-- INVALID -->
-<author>
-  <orgname>Boston Red Sox</orgname>
-  <died>1919</died>
-  <born>2004</born>
-</author>]]></programlisting>
-      </para>
-    </section>
-  </section>
-  <section xml:id="cust-attributes">
-    <title>Attributes</title>
-    <section xml:id="cust-add-attributes">
-      <title>Adding attributes</title>
-      <para>
-        The simplest way to add an attribute to a single element
-        is to add it to the attlist pattern for that element.
-        <xref dbk:linkend="ex-add-attr" dbk:xrefstyle="select: label"/>
-        adds the optional attributes <tag dbk:class="attribute">born</tag>
-        and <tag dbk:class="attribute">died</tag> to the attribute
-        list for <tag>author</tag>.
-        The <varname>db.author.attlist</varname>
-        named pattern is redefined with the
-        <tag dbk:class="attribute">combine</tag> attribute set to
-        <quote>interleave</quote>, which interleaves the two new
-        optional attributes with the existing attributes on the list.
-      </para>
-<example xml:id="ex-add-attr"><title>Adding attributes</title>
-<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns:db="http://docbook.org/ns/docbook"
-         ns="http://docbook.org/ns/docbook"
-         xmlns="http://relaxng.org/ns/structure/1.0">
-  <include href="docbook.rng"/>
-
-  <define name="db.author.attlist" combine="interleave">
-    <interleave>
-      <optional>
-        <attribute name="born">
-          <ref name="db.date.contentmodel"/>
-        </attribute>
-      </optional>
-      <optional>
-        <attribute name="died">
-          <ref name="db.date.contentmodel"/>
-        </attribute>
-      </optional>
-    </interleave>
-  </define>
-</grammar>]]></programlisting>
-<programlisting dbk:language="rnc"><![CDATA[namespace db = "http://docbook.org/ns/docbook"
-
-include "docbook.rnc" inherit = db
-
-db.author.attlist &=
-  attribute born { db.date.contentmodel }?
-  & attribute died { db.date.contentmodel }?]]></programlisting>
-</example>
-    <para>
-      Unlike
-      <xref dbk:linkend="ex-modify-element" dbk:xrefstyle="select: label"/>,
-      <xref dbk:linkend="ex-add-attr" dbk:xrefstyle="select: label"/> allows
-      the new attributes to appear on any <tag>author</tag>
-      element, not just those using the person content model.
-    </para>
-    <para>
-      <xref dbk:linkend="ex-add-attr-2" dbk:xrefstyle="select: label"/> shows
-      how you could limit the use of these attributes to authors who
-      are persons.  In this example, the new attributes are interleaved
-      with the <varname>db.person.author.contentmodel</varname>.  
-      The only difference between this example and 
-      <xref dbk:linkend="ex-modify-element" dbk:xrefstyle="select: label"/> is
-      that the added patterns are identified as attributes rather than
-      elements.  This shows some of the flexibility of RELAX NG, which
-      treats attributes and elements very consistently.
-<example xml:id="ex-add-attr-2"><title>Adding attributes; alternate method</title>
-<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns:db="http://docbook.org/ns/docbook"
-         ns="http://docbook.org/ns/docbook"
-         xmlns="http://relaxng.org/ns/structure/1.0">
-  <include href="docbook.rng"/>
-  <!-- redefinitions of named patterns -->
-  <define name="db.person.author.contentmodel" combine="interleave">
-    <interleave>
-      <optional>
-        <attribute name="born">
-          <ref name="db.date.contentmodel"/>
-        </attribute>
-      </optional>
-      <optional>
-        <attribute name="died">
-          <ref name="db.date.contentmodel"/>
-        </attribute>
-      </optional>
-    </interleave>
-  </define>
-</grammar>]]></programlisting>
-<programlisting dbk:language="rnc"><![CDATA[namespace db = "http://docbook.org/ns/docbook"
-
-include "docbook.rnc" inherit = db
-# redefinitions of named patterns
-db.person.author.contentmodel &=
-  attribute born { db.date.contentmodel }?
-  & attribute died { db.date.contentmodel }?]]></programlisting>
-</example>
-There is one difference in the treatment of attributes and elements
-that is worth noting.  By the XML 1.0 definition, the relative order
-of attributes is not significant.  Therefore, the
-<tag dbk:condition="nolink">interleave</tag> block is not required for
-attributes, though it does no harm.  
-    </para>
-    </section>
-    <section xml:id="cust-delete-attributes">
-      <title>Deleting attributes</title>
-      <para>
-        Deleting an attribute is similar to deleting an element,
-        except that you use the RELAX NG <varname>empty</varname>
-        pattern rather than <varname>notAllowed</varname>.
-        <xref dbk:linkend="ex-delete-attr" dbk:xrefstyle="select: label"/>
-        deletes the linking attributes, which are collected in the
-        <varname>db.common.linking.attributes</varname> pattern,
-        by defining that pattern as <varname>empty</varname>.
-      </para>
-<example xml:id="ex-delete-attr"><title>Deleting an attribute</title>
-<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns:db="http://docbook.org/ns/docbook"
-         ns="http://docbook.org/ns/docbook"
-         xmlns="http://relaxng.org/ns/structure/1.0">
-  <include href="docbook.rng">
-    <define name="db.common.linking.attributes">
-      <empty/>
-    </define>
-  </include>
-</grammar>]]></programlisting>
-<programlisting dbk:language="rnc"><![CDATA[namespace db = "http://docbook.org/ns/docbook"
-
-include "docbook.rnc" inherit = db {
-  db.common.linking.attributes = empty
-}]]></programlisting>
-</example>
-      <para>
-        Generally, <varname>empty</varname> is used when deleting
-        attributes and <varname>notAllowed</varname> is used when
-        deleting elements.
-      </para>
-    </section>
-    <section xml:id="cust-modify-attributes">
-      <title>Changing permitted content of attributes</title>
-      <para>
-        <xref dbk:linkend="ex-modify-attr" dbk:xrefstyle="select: label"/>
-        modifies <varname>db.spacing.enumeration</varname> to
-        add the additional value <quote>large</quote>.  Note
-        that to remove a value from an enumeration, you need
-        to redefine the entire enumeration, minus the values
-        you don't need.
-      </para>
-<example xml:id="ex-modify-attr"><title>Deleting an attribute</title>
-<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns:db="http://docbook.org/ns/docbook"
-         ns="http://docbook.org/ns/docbook"
-         xmlns="http://relaxng.org/ns/structure/1.0">
-  <include href="docbook.rng"/>
-  <!-- add value to an enumeration -->
-  <define name="db.spacing.enumeration" combine="choice">
-    <value>large</value>
-  </define>
-</grammar>]]></programlisting>
-<programlisting dbk:language="rnc"><![CDATA[namespace db = "http://docbook.org/ns/docbook"
-
-include "docbook.rnc" inherit = db
-# add value to an enumeration
-db.spacing.enumeration |= "large"]]></programlisting>
-</example>
-    </section>
-  </section>
-
-<section xml:id="cust-naming">
-<title>Naming and versioning DocBook customizations</title>
-
-<para>DocBook V5.0 is not tightly coupled with some particular
-validation technology like DTDs. This also means that DocBook V5.0
-documents don't have to (and usually don't) start with a
-document type declaration (&lt;!DOCTYPE…>) to specify the schema
-(DTD) to use. Instead, DocBook V5.0 instances can be easily
-distinguished from other XML vocabularies by using elements in the
-<uri>http://docbook.org/ns/docbook</uri> namespace. This namespace is
-enough to distinguish DocBook from other XML based formats. But the
-DocBook schema evolves over time and there are several versions of
-DocBook (e.g. 3.1, 4.2, 4.5 and 5.0).  Since DocBook version 5.0, the
-actual version used is indicated in the <tag
-dbk:class="attribute">version</tag> attribute on a root element.</para>
-
-<programlisting><![CDATA[<book xmlns="http://docbook.org/ns/docbook"
-      version="5.0">
-  …
-</book>]]></programlisting>
-
-<para>Future versions of DocBook documents will start with the same
-markup, except the version number will be raised, for example to 5.1
-or 6.0.
-The namespace will remain the same until the semantics of the elements
-change in a backward incompatible way, which is very unlikely to happen.</para>
-
-<para>If you create a DocBook schema customization you must change the <tag
-dbk:class="attribute">version</tag> attribute to distinguish your
-customization from the <quote>official</quote> DocBook.  Changing the
-namespace is not recommended because that would break the processing
-tools.  Remember that changing namespaces is the same as renaming all
-elements in the namespace.</para>
-
-<para>When you customize the schema, use the following syntax to
-identify your DocBook derivation:</para>
-
-<programlisting><replaceable>base_version</replaceable>-[subset|extension|variant] [<replaceable>name</replaceable>[-<replaceable>version</replaceable>]?]+</programlisting>
-
-<para>For example:</para>
-
-<programlisting>5.0-subset simplified-1.0
-5.0-variant ASMBook
-5.0-variant ASMBook-2006
-5.0-extension MathML-2.0 SVG-1.1</programlisting>
-
-<para>The first part of the version identifier is the version number of the
-DocBook schema from which you derived your customization.</para>
-
-<para>If your schema is a proper subset, you can advertise this status
-by using the <literal>subset</literal> keyword in the description. If
-your schema contains any markup model extensions, you can advertise
-this status by using the <literal>extension</literal> keyword. If
-you'd rather not characterize your variant specifically as a subset or
-an extension, use the <literal>variant</literal> keyword.</para>
-
-<para>After these keywords you may add a whitespace separated list of
-customization identifiers. Each name may be optionally followed by its
-version number.</para>
-
-</section>
-
-</section>
-
-<section xml:id="faq">
-<title>FAQ</title>
-
-<qandaset>
-<qandadiv>
-<title>Authoring</title>
-
-<qandaentry xml:id="faq-authoring-schema-association">
-<question>
-<para>How do I attach a schema to a DocBook V5.0 document when I do not
-want to use DTDs and !DOCTYPE?</para>
-</question>
-<answer>
-<para>There is no standard way of associating a RELAX NG schema with a
-document. Most tools provide some mechanism for performing this
-association, consult the documentation for your application. In some
-tools you must specify schema manually each time you want to
-edit/process your document.</para>
-</answer>
-</qandaentry>
-
-<qandaentry xml:id="faq-authoring-general-entities">
-<question>
-<para>How do I use entities like <tag dbk:class="genentity">ndash</tag> in
-DocBook V5.0?</para>
-</question>
-<answer>
-<para>Modern schema languages (including RELAX NG and W3X XML Schema)
-do not provide any means to define entities that can be used for easier
-typing of special characters. Some editors provide functions or
-special toolbars that allow you to easily pick necessary character
-and insert it into document as a raw Unicode character or a numeric
-character reference.</para>
-<para>Another possibility is to include entity definitions in the
-prolog of your document. <link
-xl:href="http://www.w3.org/2003/entities/">Entity definition
-files</link> are now maintained by W3C. You can reference definition
-files with entity definitions you are interested in and then reference
-imported entities. For example:</para>
-<programlisting><![CDATA[<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE article [
-<!ENTITY % isopub SYSTEM "http://www.w3.org/2003/entities/iso8879/isopub.ent">
-%isopub;
-]>
-<article xmlns="http://docbook.org/ns/docbook" version="5.0">
-<title>DocBook V5.0 &ndash; the superb documentation format</title>]]>
-…</programlisting>
-<para>For your convenience there is also flattened entity definition
-file which contains all entity definitions.</para>
-<programlisting><![CDATA[<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE article [
-<!ENTITY % allent SYSTEM "http://www.w3.org/2003/entities/2007/w3centities-f.ent">
-%allent;
-]>
-<article xmlns="http://docbook.org/ns/docbook" version="5.0">
-<title>DocBook V5.0 &ndash; the superb documentation format</title>]]>
-…</programlisting>
-</answer>
-</qandaentry>
-
-<qandaentry xml:id="faq-authoring-modularization">
-<question>
-<para>How to modularize documents?</para>
-</question>
-<answer>
-<para>You can use <link
-xl:href="http://www.w3.org/TR/xinclude/">XInclude</link> for this
-task. There is an alternative schema for DocBook V5.0 that
-contains XInclude elements. This is necessary to make some XML editors
-happy. This schema can be found in files that end with letters <quote>xi</quote>, e.g.
-<filename>docbookxi.rnc</filename> instead of
-<filename>docbook.rnc</filename>.</para>
-</answer>
-</qandaentry>
-
-<qandaentry xml:id="faq-authoring-validating-xincludes">
-<question>
-<para>How to validate documents which are composed by XInclude?</para>
-</question>
-<answer>
-<para>If you are using XIncludes you should make sure that the final
-document after resolving all inclusions is valid DocBook V5.0
-instance. This means that all XIncludes should be processed before
-validation takes place. The following command can be used to enable
-XInclude processing in oNVDL.</para>
-<screen><command>java</command> -Dorg.apache.xerces.xni.parser.XMLParserConfiguration=org.apache.xerces.parsers.XIncludeParserConfiguration -jar <replaceable>/path/to/oNVDL/</replaceable>bin/onvdl.jar <replaceable>/path/to/</replaceable>docbook.nvdl document.xml</screen>
-<para>For JNVDL you can use switch <option>-xi</option> to enable XInclude processing.</para>
-</answer>
-</qandaentry>
-
-</qandadiv>
-
-<qandadiv>
-<title>Stylesheets</title>
-
-<qandaentry xml:id="faq-stylesheets-future">
-<question>
-<para>Will the current DocBook XSL stylesheets (XSLT 1.0 based
-implementation) be maintained and improved in the future since work on
-a new XSLT 2.0 based implementation has started?</para>
-</question>
-<answer>
-<para>Yes, the current stylesheets (like 1.73.x) will be supported and
-improved further because they are very widely deployed and work with
-many existing XSLT processors.</para>
-<para>Surely there will be a point in a future when all new development
-will be switched to the XSLT 2.0 based implementation. But this
-will not happen until all features of the current stylesheets are
-implemented in the new stylesheets, and until there is more than
-one usable XSLT 2.0 processor available.</para>
-</answer>
-</qandaentry>
-
-</qandadiv>
-
-<qandadiv>
-<title>Schema customizations</title>
-
-<qandaentry xml:id="faq-customization-mathml">
-<question>
-<para>How can I extend the DocBook schema with MathML elements?</para>
-</question>
-<answer>
-<para>The basic DocBook schema allows elements from the MathML namespace
-to appear inside the <tag>equation</tag> element.  This means that you can
-validate a DocBook+MathML document, but MathML content will be ignored
-during the validation. You will also not be able to use guided editing
-for the MathML content.</para>
-<para>If you need strict validation of MathML content or guided
-editing for MathML, you can easily extend the base DocBook schema with
-the MathML schema.</para>
-<procedure>
-<title>Extending the DocBook schema with the MathML schema</title>
-<step>
-<para>Download the MathML RELAX NG schema from <link
-xl:href="http://yupotan.sppd.ne.jp/relax-ng/mml2.html"/> and unpack it
-somewhere (e.g. into a <filename>mathml</filename> subdirectory).</para>
-</step>
-<step>
-<para>Create a schema customization in compact syntax—<filename>dbmathml.rnc</filename>:</para>
-<programlisting dbk:language="rnc">namespace html = "http://www.w3.org/1999/xhtml"
-namespace mml = "http://www.w3.org/1998/Math/MathML"
-namespace db = "http://docbook.org/ns/docbook"
-
-include "/path/to/docbook.rnc" {
-  db._any.mml = external "mathml/mathml2.rnc"
-  db._any =
-    element * - (db:* | html:* | mml:*) {
-      (attribute * { text }
-       | text
-       | db._any)*
-    }
-}</programlisting>
-<para>Or, alternatively, you can use the XML syntax of RELAX NG—<filename>dbmathml.rng</filename>:</para>
-<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns="http://relaxng.org/ns/structure/1.0">
-
-<include href="/path/to/docbook.rng">
-  <define name="db._any.mml">
-    <externalRef href="mathml/mathml2.rng"/>
-  </define>
-
-  <define name="db._any">
-    <element>
-      <anyName>
-        <except>
-          <nsName ns="http://docbook.org/ns/docbook"/>
-          <nsName ns="http://www.w3.org/1999/xhtml"/>
-          <nsName ns="http://www.w3.org/1998/Math/MathML"/>
-        </except>
-      </anyName>
-      <zeroOrMore>
-        <choice>
-          <attribute>
-            <anyName/>
-          </attribute>
-          <text/>
-          <ref name="db._any"/>
-        </choice>
-      </zeroOrMore>
-    </element>
-  </define>
-</include>
-
-</grammar>]]></programlisting>
-</step>
-<step>
-<para>Now use the customized schema (<filename>dbmathml.rnc</filename>
-or <filename>dbmathml.rng</filename>) instead of the original
-DocBook schema.</para>
-</step>
-</procedure>
-</answer>
-</qandaentry>
-
-<qandaentry xml:id="faq-customization-svg">
-<question>
-<para>How can I extend the DocBook schema with SVG elements?</para>
-</question>
-<answer>
-<para>The situation is the same as with MathML support. You can use
-elements from the SVG namespace inside the <tag>imageobject</tag>
-element.</para>
-<procedure>
-<title>Extending the DocBook schema with the SVG schema</title>
-<step>
-<para>Download the SVG RELAX NG schema from <link
-xl:href="http://www.w3.org/Graphics/SVG/1.1/rng/rng.zip"/> and unpack it
-somewhere (e.g. into an <filename>svg</filename> subdirectory).</para>
-</step>
-<step>
-<para>Create a schema customization in compact syntax—<filename>dbsvg.rnc</filename>:</para>
-<programlisting dbk:language="rnc">namespace html = "http://www.w3.org/1999/xhtml"
-namespace db = "http://docbook.org/ns/docbook"
-namespace svg = "http://www.w3.org/2000/svg"
-
-include "/path/to/docbook.rnc" {
-  db._any.svg = external "svg/svg11.rnc"
-  db._any =
-    element * - (db:* | html:* | svg:*) {
-      (attribute * { text }
-       | text
-       | db._any)*
-    }
-}</programlisting>
-<para>Or, alternatively, you can use the XML syntax of RELAX NG—<filename>dbsvg.rng</filename>:</para>
-<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns="http://relaxng.org/ns/structure/1.0">
-
-<include href="/path/to/docbook.rng">
-  <define name="db._any.svg">
-    <externalRef href="svg/svg11.rng"/>
-  </define>
-
-  <define name="db._any">
-    <element>
-      <anyName>
-        <except>
-          <nsName ns="http://docbook.org/ns/docbook"/>
-          <nsName ns="http://www.w3.org/1999/xhtml"/>
-          <nsName ns="http://www.w3.org/2000/svg"/>
-        </except>
-      </anyName>
-      <zeroOrMore>
-        <choice>
-          <attribute>
-            <anyName/>
-          </attribute>
-          <text/>
-          <ref name="db._any"/>
-        </choice>
-      </zeroOrMore>
-    </element>
-  </define>
-</include>
-
-</grammar>]]></programlisting>
-</step>
-<step>
-<para>Now use the customized schema (<filename>dbsvg.rnc</filename>
-or <filename>dbsvg.rng</filename>) instead of the original
-DocBook schema.</para>
-</step>
-</procedure>
-</answer>
-</qandaentry>
-
-<qandaentry xml:id="faq-customization-mathml-svg">
-<question>
-<para>Is it possible to use the previous two customizations for MathML
-and SVG together?</para>
-</question>
-<answer>
-<para>Yes, you can create a special schema customization that combines
-both MathML and SVG with the DocBook schema. In compact syntax, the merged
-schema is:</para>
-<programlisting dbk:language="rnc">namespace html = "http://www.w3.org/1999/xhtml"
-namespace mml = "http://www.w3.org/1998/Math/MathML"
-namespace db = "http://docbook.org/ns/docbook"
-namespace svg = "http://www.w3.org/2000/svg"
-
-include "/path/to/docbook.rnc" {
-  db._any.mml = external "mahtml/mathml2.rnc"
-  db._any.svg = external "svg/svg11.rnc"
-  db._any =
-    element * - (db:* | html:* | mml:* | svg:*) {
-      (attribute * { text }
-       | text
-       | db._any)*
-    }
-}</programlisting>
-<para>Or alternatively in the full RELAX NG syntax:</para>
-<programlisting dbk:language="rng"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns="http://relaxng.org/ns/structure/1.0">
-
-<include href="/path/to/docbook.rng">
-  <define name="db._any.mml">
-    <externalRef href="mathml/mathml2.rng"/>
-  </define>
-
-  <define name="db._any.svg">
-    <externalRef href="svg/svg11.rng"/>
-  </define>
-
-  <define name="db._any">
-    <element>
-      <anyName>
-        <except>
-          <nsName ns="http://docbook.org/ns/docbook"/>
-          <nsName ns="http://www.w3.org/1999/xhtml"/>
-          <nsName ns="http://www.w3.org/1998/Math/MathML"/>
-          <nsName ns="http://www.w3.org/2000/svg"/>
-        </except>
-      </anyName>
-      <zeroOrMore>
-        <choice>
-          <attribute>
-            <anyName/>
-          </attribute>
-          <text/>
-          <ref name="db._any"/>
-        </choice>
-      </zeroOrMore>
-    </element>
-  </define>
-</include>
-
-</grammar>]]></programlisting>
-</answer>
-</qandaentry>
-
-<qandaentry xml:id="faq-customization-links">
-<question>
-<para>Are there any other examples of schema customization
-available?</para>
-</question>
-<answer>
-<para>Sure. Some of the are listed bellow:</para>
-<itemizedlist>
-<listitem><para><link
-xl:href="http://www.w3.org/TR/xml-i18n-bp/#docbook-plus-its">Sample
-customization of ITS and DocBook</link></para></listitem>
-<listitem><para><link
-xl:href="http://wiki.docbook.org/topic/DocbookSchemas">Examples on
-DocBook WiKi</link></para></listitem>
-</itemizedlist>
-</answer>
-</qandaentry>
-
-</qandadiv>
-
-<qandadiv>
-<title>Tool specific problems</title>
-
-<qandaentry xml:id="faq-tools-xmlspy-xmlid">
-<question>
-<para>I'm using Altova XMLSpy to validate DocBook V5.0 instances
-against the W3C XML Schema (<filename>docbook.xsd</filename>). XMLSpy
-complains about undefined <tag dbk:class="attribute">xml:id</tag>
-attributes?</para>
-</question>
-<answer>
-<para>XMLSpy always uses its own bundled version of
-<filename>xml.xsd</filename> which unfortunately doesn't define the <tag
-dbk:class="attribute">xml:id</tag> attribute. The bundled version of
-<filename>xml.xsd</filename> is hardwired into the program and cannot
-be replaced by a newer version. To solve this problem you must upgrade
-to version 2006 SP1.</para>
-</answer>
-</qandaentry>
-
-</qandadiv>
-
-</qandaset>
-</section>
-
-<bibliography xml:id="references">
-
-<bibliomixed>
-<abbrev>RNCTUT</abbrev>
-Clark, James – Cowan, John – MURATA, Makoto: <title>RELAX NG Compact Syntax Tutorial</title>.
-Working Draft, 26 March 2003. OASIS. <bibliomisc><link xl:href="http://relaxng.org/compact-tutorial-20030326.html"/></bibliomisc>
-</bibliomixed>
-
-<bibliomixed>
-<abbrev>NVDLTUT</abbrev>
-Nálevka, Petr:
-<title>NVDL Tutorial</title>.
-<bibliomisc><link xl:href="http://jnvdl.sourceforge.net/tutorial.html"/></bibliomisc>
-</bibliomixed>
-
-<bibliomixed>
-<abbrev>XMLID</abbrev>
-Marsh, Jonathan – 
-Veillard, Daniel –
-Walsh, Norman: <title>xml:id Version 1.0</title>. W3C Recommendation, 9 September 2005. <bibliomisc><link xl:href="http://www.w3.org/TR/xml-id/"/></bibliomisc>
-</bibliomixed>
-
-<bibliomixed>
-<abbrev>DB5SPEC</abbrev>
-Norman, Walsh: <title>The DocBook Schema</title>.
-Working Draft 5.0a1, OASIS, 29 June 2005.
-<bibliomisc><link xl:href="http://www.docbook.org/specs/wd-docbook-docbook-5.0a1.html"/></bibliomisc>
-</bibliomixed>
-
-</bibliography>
-</article>
-</book>
diff --git a/org.argeo.server.jcr/ext/test/org/argeo/jcr/tabular/JcrTabularTest.java b/org.argeo.server.jcr/ext/test/org/argeo/jcr/tabular/JcrTabularTest.java
deleted file mode 100644 (file)
index 8896b2f..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr.tabular;
-
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.jcr.Node;
-import javax.jcr.PropertyType;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.commons.cnd.CndImporter;
-import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.ArgeoTypes;
-import org.argeo.util.tabular.TabularColumn;
-import org.argeo.util.tabular.TabularRow;
-import org.argeo.util.tabular.TabularRowIterator;
-import org.argeo.util.tabular.TabularWriter;
-
-public class JcrTabularTest extends AbstractJackrabbitTestCase {
-       private final static Log log = LogFactory.getLog(JcrTabularTest.class);
-
-       public void testWriteReadCsv() throws Exception {
-               session().setNamespacePrefix("argeo", ArgeoNames.ARGEO_NAMESPACE);
-               InputStreamReader reader = new InputStreamReader(getClass()
-                               .getResourceAsStream("/org/argeo/jcr/argeo.cnd"));
-               CndImporter.registerNodeTypes(reader, session());
-               reader.close();
-
-               // write
-               Integer columnCount = 15;
-               Long rowCount = 1000l;
-               String stringValue = "test, \ntest";
-
-               List<TabularColumn> header = new ArrayList<TabularColumn>();
-               for (int i = 0; i < columnCount; i++) {
-                       header.add(new TabularColumn("col" + i, PropertyType.STRING));
-               }
-               Node tableNode = session().getRootNode().addNode("table",
-                               ArgeoTypes.ARGEO_TABLE);
-               TabularWriter writer = new JcrTabularWriter(tableNode, header,
-                               ArgeoTypes.ARGEO_CSV);
-               for (int i = 0; i < rowCount; i++) {
-                       List<Object> objs = new ArrayList<Object>();
-                       for (int j = 0; j < columnCount; j++) {
-                               objs.add(stringValue);
-                       }
-                       writer.appendRow(objs.toArray());
-               }
-               writer.close();
-               session().save();
-
-               if (log.isDebugEnabled())
-                       log.debug("Wrote tabular content " + rowCount + " rows, "
-                                       + columnCount + " columns");
-               // read
-               TabularRowIterator rowIt = new JcrTabularRowIterator(tableNode);
-               Long count = 0l;
-               while (rowIt.hasNext()) {
-                       TabularRow tr = rowIt.next();
-                       assertEquals(header.size(), tr.size());
-                       count++;
-               }
-               assertEquals(rowCount, count);
-               if (log.isDebugEnabled())
-                       log.debug("Read tabular content " + rowCount + " rows, "
-                                       + columnCount + " columns");
-       }
-}
diff --git a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/JcrResourceAdapterTest.java b/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/JcrResourceAdapterTest.java
deleted file mode 100644 (file)
index 3ce499a..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.server.jcr;
-
-import java.io.InputStream;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.List;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase;
-import org.argeo.jcr.JcrResourceAdapter;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.Resource;
-
-public class JcrResourceAdapterTest extends AbstractJackrabbitTestCase {
-       private static SimpleDateFormat sdf = new SimpleDateFormat(
-                       "yyyyMMdd:hhmmss.SSS");
-
-       private final static Log log = LogFactory
-                       .getLog(JcrResourceAdapterTest.class);
-
-       private JcrResourceAdapter jra;
-
-       public void testCreate() throws Exception {
-               String basePath = "/test/subdir";
-               jra.mkdirs(basePath);
-               Resource res = new ClassPathResource("org/argeo/server/jcr/dummy00.xls");
-               String filePath = basePath + "/dummy.xml";
-               jra.create(filePath, res.getInputStream(), "application/vnd.ms-excel");
-               InputStream in = jra.retrieve(filePath);
-               assertTrue(IOUtils.contentEquals(res.getInputStream(), in));
-       }
-
-       public void testVersioning() throws Exception {
-               String basePath = "/test/versions";
-               jra.mkdirs(basePath);
-               String filePath = basePath + "/dummy.xml";
-               Resource res00 = new ClassPathResource(
-                               "org/argeo/server/jcr/dummy00.xls");
-               jra.create(filePath, res00.getInputStream(), "application/vnd.ms-excel");
-               Resource res01 = new ClassPathResource(
-                               "org/argeo/server/jcr/dummy01.xls");
-               jra.update(filePath, res01.getInputStream());
-               Resource res02 = new ClassPathResource(
-                               "org/argeo/server/jcr/dummy02.xls");
-               jra.update(filePath, res02.getInputStream());
-
-               List<Calendar> versions = jra.listVersions(filePath);
-               log.debug("Versions of " + filePath);
-               int count = 0;
-               for (Calendar version : versions) {
-                       log.debug(" " + (count == 0 ? "base" : count - 1) + "\t"
-                                       + sdf.format(version.getTime()));
-                       count++;
-               }
-
-               assertEquals(4, versions.size());
-
-               InputStream in = jra.retrieve(filePath, 1);
-               assertTrue(IOUtils.contentEquals(res01.getInputStream(), in));
-               in = jra.retrieve(filePath, 0);
-               assertTrue(IOUtils.contentEquals(res00.getInputStream(), in));
-               in = jra.retrieve(filePath, 2);
-               assertTrue(IOUtils.contentEquals(res02.getInputStream(), in));
-               Resource res03 = new ClassPathResource(
-                               "org/argeo/server/jcr/dummy03.xls");
-               jra.update(filePath, res03.getInputStream());
-               in = jra.retrieve(filePath, 1);
-               assertTrue(IOUtils.contentEquals(res01.getInputStream(), in));
-       }
-
-       @Override
-       protected void setUp() throws Exception {
-               log.debug("SET UP");
-               super.setUp();
-               jra = new JcrResourceAdapter();
-               jra.setSession(session());
-       }
-
-       @Override
-       protected void tearDown() throws Exception {
-               log.debug("TEAR DOWN");
-               super.tearDown();
-       }
-}
diff --git a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy00.xls b/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy00.xls
deleted file mode 100644 (file)
index e5846fe..0000000
Binary files a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy00.xls and /dev/null differ
diff --git a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy01.xls b/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy01.xls
deleted file mode 100644 (file)
index b5c6b55..0000000
Binary files a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy01.xls and /dev/null differ
diff --git a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy02.xls b/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy02.xls
deleted file mode 100644 (file)
index d73bc66..0000000
Binary files a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy02.xls and /dev/null differ
diff --git a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy03.xls b/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy03.xls
deleted file mode 100644 (file)
index 0759cb9..0000000
Binary files a/org.argeo.server.jcr/ext/test/org/argeo/server/jcr/dummy03.xls and /dev/null differ
diff --git a/org.argeo.server.jcr/pom.xml b/org.argeo.server.jcr/pom.xml
deleted file mode 100644 (file)
index af01e00..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-       <modelVersion>4.0.0</modelVersion>
-       <parent>
-               <groupId>org.argeo.commons</groupId>
-               <artifactId>argeo-commons</artifactId>
-               <version>2.1.46-SNAPSHOT</version>
-               <relativePath>..</relativePath>
-       </parent>
-       <artifactId>org.argeo.server.jcr</artifactId>
-       <name>Commons JCR</name>
-       <dependencies>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.util</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-       </dependencies>
-</project>
\ No newline at end of file
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitAdminLoginModule.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitAdminLoginModule.java
deleted file mode 100644 (file)
index 7e17dbc..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.argeo.jackrabbit;
-
-import java.util.Map;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.login.LoginException;
-import javax.security.auth.spi.LoginModule;
-
-import org.apache.jackrabbit.core.security.SecurityConstants;
-import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
-
-public class JackrabbitAdminLoginModule implements LoginModule {
-       private Subject subject;
-
-       @Override
-       public void initialize(Subject subject, CallbackHandler callbackHandler,
-                       Map<String, ?> sharedState, Map<String, ?> options) {
-               this.subject = subject;
-       }
-
-       @Override
-       public boolean login() throws LoginException {
-               // TODO check permission?
-               return true;
-       }
-
-       @Override
-       public boolean commit() throws LoginException {
-               subject.getPrincipals().add(
-                               new AdminPrincipal(SecurityConstants.ADMIN_ID));
-               return true;
-       }
-
-       @Override
-       public boolean abort() throws LoginException {
-               return true;
-       }
-
-       @Override
-       public boolean logout() throws LoginException {
-               subject.getPrincipals().removeAll(
-                               subject.getPrincipals(AdminPrincipal.class));
-               return true;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitContainer.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitContainer.java
deleted file mode 100644 (file)
index c5ca795..0000000
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jackrabbit;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.UUID;
-
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.api.JackrabbitRepository;
-import org.apache.jackrabbit.core.RepositoryImpl;
-import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
-import org.argeo.jcr.ArgeoJcrException;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.JcrUtils;
-import org.springframework.core.io.Resource;
-import org.springframework.util.SystemPropertyUtils;
-import org.xml.sax.InputSource;
-
-/**
- * Wrapper around a Jackrabbit repository which allows to configure it in Spring
- * and expose it as a {@link Repository}.
- */
-@Deprecated
-public class JackrabbitContainer extends JackrabbitWrapper {
-       private final static Log log = LogFactory.getLog(JackrabbitContainer.class);
-
-       // local
-       private Resource configuration;
-
-       private Resource variables;
-
-       private RepositoryConfig repositoryConfig;
-       private File homeDirectory;
-       private Boolean inMemory = false;
-
-       /** Migrations to execute (if not already done) */
-       private Set<JackrabbitDataModelMigration> dataModelMigrations = new HashSet<JackrabbitDataModelMigration>();
-
-       /** Straight (non spring) values */
-       private Properties configurationProperties;
-       private InputSource configurationXml;
-
-       /**
-        * Empty constructor, {@link #init()} should be called after properties have
-        * been set
-        */
-       public JackrabbitContainer() {
-       }
-
-       public void init() {
-               // long begin = System.currentTimeMillis();
-
-               if (getRepository() != null)
-                       throw new ArgeoJcrException("Cannot be used to wrap another repository");
-               Repository repository = createJackrabbitRepository();
-               super.setRepository(repository);
-
-               // migrate if needed
-               migrate();
-
-               // apply new CND files after migration
-               prepareDataModel();
-
-               // double duration = ((double) (System.currentTimeMillis() - begin)) /
-               // 1000;
-               // if (log.isDebugEnabled())
-               // log.debug("Initialized JCR repository wrapper in " + duration
-               // + " s");
-       }
-
-       /** Actually creates the new repository. */
-       protected Repository createJackrabbitRepository() {
-               long begin = System.currentTimeMillis();
-               InputStream configurationIn = null;
-               Repository repository;
-               try {
-                       // temporary
-                       if (inMemory && getHomeDirectory().exists()) {
-                               FileUtils.deleteDirectory(getHomeDirectory());
-                               log.warn("Deleted Jackrabbit home directory " + getHomeDirectory());
-                       }
-
-                       // process configuration file
-                       Properties vars = getConfigurationProperties();
-                       vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE, getHomeDirectory().getCanonicalPath());
-                       InputSource is;
-                       if (configurationXml != null)
-                               is = configurationXml;
-                       else {
-                               configurationIn = readConfiguration();
-                               is = new InputSource(configurationIn);
-                       }
-                       repositoryConfig = RepositoryConfig.create(is, vars);
-
-                       //
-                       // Actual repository creation
-                       //
-                       repository = RepositoryImpl.create(repositoryConfig);
-
-                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
-                       if (log.isTraceEnabled())
-                               log.trace("Created Jackrabbit repository in " + duration + " s, home: " + getHomeDirectory());
-
-                       return repository;
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot create Jackrabbit repository " + getHomeDirectory(), e);
-               } finally {
-                       IOUtils.closeQuietly(configurationIn);
-               }
-       }
-
-       /** Lazy init. */
-       protected File getHomeDirectory() {
-               try {
-                       if (homeDirectory == null) {
-                               if (inMemory) {
-                                       homeDirectory = new File(System.getProperty("java.io.tmpdir") + File.separator
-                                                       + System.getProperty("user.name") + File.separator + "jackrabbit-" + UUID.randomUUID());
-                                       homeDirectory.mkdirs();
-                                       // will it work if directory is not empty??
-                                       homeDirectory.deleteOnExit();
-                               }
-                       }
-
-                       return homeDirectory.getCanonicalFile();
-               } catch (IOException e) {
-                       throw new ArgeoJcrException("Cannot get canonical file for " + homeDirectory, e);
-               }
-       }
-
-       /** Executes migrations, if needed. */
-       protected void migrate() {
-               // No migration to perform
-               if (dataModelMigrations.size() == 0)
-                       return;
-
-               Boolean restartAndClearCaches = false;
-
-               // migrate data
-               Session session = null;
-               try {
-                       session = login();
-                       for (JackrabbitDataModelMigration dataModelMigration : new TreeSet<JackrabbitDataModelMigration>(
-                                       dataModelMigrations)) {
-                               if (dataModelMigration.migrate(session)) {
-                                       restartAndClearCaches = true;
-                               }
-                       }
-               } catch (ArgeoJcrException e) {
-                       throw e;
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot migrate", e);
-               } finally {
-                       JcrUtils.logoutQuietly(session);
-               }
-
-               // restart repository
-               if (restartAndClearCaches) {
-                       Repository repository = getRepository();
-                       if (repository instanceof RepositoryImpl) {
-                               JackrabbitDataModelMigration.clearRepositoryCaches(((RepositoryImpl) repository).getConfig());
-                       }
-                       ((JackrabbitRepository) repository).shutdown();
-                       createJackrabbitRepository();
-               }
-
-               // set data model version
-               try {
-                       session = login();
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot login to migrated repository", e);
-               }
-
-               for (JackrabbitDataModelMigration dataModelMigration : new TreeSet<JackrabbitDataModelMigration>(
-                               dataModelMigrations)) {
-                       try {
-                               if (session.itemExists(dataModelMigration.getDataModelNodePath())) {
-                                       Node dataModelNode = session.getNode(dataModelMigration.getDataModelNodePath());
-                                       dataModelNode.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION,
-                                                       dataModelMigration.getTargetVersion());
-                                       session.save();
-                               }
-                       } catch (Exception e) {
-                               log.error("Cannot set model version", e);
-                       }
-               }
-               JcrUtils.logoutQuietly(session);
-
-       }
-
-       /** Shutdown the repository */
-       public void destroy() throws Exception {
-               Repository repository = getRepository();
-               if (repository != null && repository instanceof RepositoryImpl) {
-                       long begin = System.currentTimeMillis();
-                       ((RepositoryImpl) repository).shutdown();
-                       if (inMemory)
-                               if (getHomeDirectory().exists()) {
-                                       FileUtils.deleteDirectory(getHomeDirectory());
-                                       if (log.isDebugEnabled())
-                                               log.debug("Deleted Jackrabbit home directory " + getHomeDirectory());
-                               }
-                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
-                       if (log.isTraceEnabled())
-                               log.trace("Destroyed Jackrabbit repository in " + duration + " s, home: " + getHomeDirectory());
-               }
-               repository = null;
-       }
-
-       public void dispose() {
-               throw new IllegalArgumentException("Call destroy() method instead of dispose()");
-       }
-
-       /*
-        * UTILITIES
-        */
-       /**
-        * Reads the configuration which will initialize a {@link RepositoryConfig}.
-        */
-       protected InputStream readConfiguration() {
-               try {
-                       return configuration != null ? configuration.getInputStream() : null;
-               } catch (IOException e) {
-                       throw new ArgeoJcrException("Cannot read Jackrabbit configuration " + configuration, e);
-               }
-       }
-
-       /**
-        * Reads the variables which will initialize a {@link Properties}. Returns
-        * null by default, to be overridden.
-        * 
-        * @return a new stream or null if no variables available
-        */
-       protected InputStream readVariables() {
-               try {
-                       return variables != null ? variables.getInputStream() : null;
-               } catch (IOException e) {
-                       throw new ArgeoJcrException("Cannot read Jackrabbit variables " + variables, e);
-               }
-       }
-
-       /**
-        * Resolves ${} placeholders in the provided string. Based on system
-        * properties if no map is provided.
-        */
-       protected String resolvePlaceholders(String string, Map<String, String> variables) {
-               return SystemPropertyUtils.resolvePlaceholders(string);
-       }
-
-       /** Generates the properties to use in the configuration. */
-       protected Properties getConfigurationProperties() {
-               if (configurationProperties != null)
-                       return configurationProperties;
-
-               InputStream propsIn = null;
-               Properties vars;
-               try {
-                       vars = new Properties();
-                       propsIn = readVariables();
-                       if (propsIn != null) {
-                               vars.load(propsIn);
-                       }
-                       // resolve system properties
-                       for (Object key : vars.keySet()) {
-                               // TODO: implement a smarter mechanism to resolve nested ${}
-                               String newValue = resolvePlaceholders(vars.getProperty(key.toString()), null);
-                               vars.put(key, newValue);
-                       }
-                       // override with system properties
-                       vars.putAll(System.getProperties());
-
-                       if (log.isTraceEnabled()) {
-                               log.trace("Jackrabbit config variables:");
-                               for (Object key : new TreeSet<Object>(vars.keySet()))
-                                       log.trace(key + "=" + vars.getProperty(key.toString()));
-                       }
-
-               } catch (IOException e) {
-                       throw new ArgeoJcrException("Cannot read configuration properties", e);
-               } finally {
-                       IOUtils.closeQuietly(propsIn);
-               }
-               return vars;
-       }
-
-       /*
-        * FIELDS ACCESS
-        */
-
-       public void setHomeDirectory(File homeDirectory) {
-               this.homeDirectory = homeDirectory;
-       }
-
-       public void setInMemory(Boolean inMemory) {
-               this.inMemory = inMemory;
-       }
-
-       public void setRepository(Repository repository) {
-               throw new ArgeoJcrException("Cannot be used to wrap another repository");
-       }
-
-       public void setDataModelMigrations(Set<JackrabbitDataModelMigration> dataModelMigrations) {
-               this.dataModelMigrations = dataModelMigrations;
-       }
-
-       public void setVariables(Resource variables) {
-               this.variables = variables;
-       }
-
-       public void setConfiguration(Resource configuration) {
-               this.configuration = configuration;
-       }
-
-       public void setConfigurationProperties(Properties configurationProperties) {
-               this.configurationProperties = configurationProperties;
-       }
-
-       public void setConfigurationXml(InputSource configurationXml) {
-               this.configurationXml = configurationXml;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitDataModelMigration.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitDataModelMigration.java
deleted file mode 100644 (file)
index 53f0e44..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jackrabbit;
-
-import java.io.InputStreamReader;
-import java.io.Reader;
-
-import javax.jcr.Node;
-import javax.jcr.Session;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.commons.cnd.CndImporter;
-import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.argeo.jcr.ArgeoJcrException;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.JcrCallback;
-import org.argeo.jcr.JcrUtils;
-import org.springframework.core.io.Resource;
-
-/** Migrate the data in a Jackrabbit repository. */
-public class JackrabbitDataModelMigration implements
-               Comparable<JackrabbitDataModelMigration> {
-       private final static Log log = LogFactory
-                       .getLog(JackrabbitDataModelMigration.class);
-
-       private String dataModelNodePath;
-       private String targetVersion;
-       private Resource migrationCnd;
-       private JcrCallback dataModification;
-
-       /**
-        * Expects an already started repository with the old data model to migrate.
-        * Expects to be run with admin rights (Repository.login() will be used).
-        * 
-        * @return true if a migration was performed and the repository needs to be
-        *         restarted and its caches cleared.
-        */
-       public Boolean migrate(Session session) {
-               long begin = System.currentTimeMillis();
-               Reader reader = null;
-               try {
-                       // check if already migrated
-                       if (!session.itemExists(dataModelNodePath)) {
-                               log.warn("Node " + dataModelNodePath
-                                               + " does not exist: nothing to migrate.");
-                               return false;
-                       }
-                       Node dataModelNode = session.getNode(dataModelNodePath);
-                       if (dataModelNode.hasProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION)) {
-                               String currentVersion = dataModelNode.getProperty(
-                                               ArgeoNames.ARGEO_DATA_MODEL_VERSION).getString();
-                               if (compareVersions(currentVersion, targetVersion) >= 0) {
-                                       log.info("Data model at version " + currentVersion
-                                                       + ", no need to migrate.");
-                                       return false;
-                               }
-                       }
-
-                       // apply transitional CND
-                       if (migrationCnd != null) {
-                               reader = new InputStreamReader(migrationCnd.getInputStream());
-                               CndImporter.registerNodeTypes(reader, session, true);
-                               session.save();
-                               log.info("Registered migration node types from " + migrationCnd);
-                       }
-
-                       // modify data
-                       dataModification.execute(session);
-
-                       // apply changes
-                       session.save();
-
-                       long duration = System.currentTimeMillis() - begin;
-                       log.info("Migration of data model " + dataModelNodePath + " to "
-                                       + targetVersion + " performed in " + duration + "ms");
-                       return true;
-               } catch (Exception e) {
-                       JcrUtils.discardQuietly(session);
-                       throw new ArgeoJcrException("Migration of data model "
-                                       + dataModelNodePath + " to " + targetVersion + " failed.",
-                                       e);
-               } finally {
-                       JcrUtils.logoutQuietly(session);
-                       IOUtils.closeQuietly(reader);
-               }
-       }
-
-       protected static int compareVersions(String version1, String version2) {
-               // TODO do a proper version analysis and comparison
-               return version1.compareTo(version2);
-       }
-
-       /** To be called on a stopped repository. */
-       public static void clearRepositoryCaches(RepositoryConfig repositoryConfig) {
-               try {
-                       String customeNodeTypesPath = "/nodetypes/custom_nodetypes.xml";
-                       // FIXME causes weird error in Eclipse
-                       //repositoryConfig.getFileSystem().deleteFile(customeNodeTypesPath);
-                       if (log.isDebugEnabled())
-                               log.debug("Cleared " + customeNodeTypesPath);
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot clear caches", e);
-               }
-
-               // File customNodeTypes = new File(home.getPath()
-               // + "/repository/nodetypes/custom_nodetypes.xml");
-               // if (customNodeTypes.exists()) {
-               // customNodeTypes.delete();
-               // if (log.isDebugEnabled())
-               // log.debug("Cleared " + customNodeTypes);
-               // } else {
-               // log.warn("File " + customNodeTypes + " not found.");
-               // }
-       }
-
-       /*
-        * FOR USE IN (SORTED) SETS
-        */
-
-       public int compareTo(JackrabbitDataModelMigration dataModelMigration) {
-               // TODO make ordering smarter
-               if (dataModelNodePath.equals(dataModelMigration.dataModelNodePath))
-                       return compareVersions(targetVersion,
-                                       dataModelMigration.targetVersion);
-               else
-                       return dataModelNodePath
-                                       .compareTo(dataModelMigration.dataModelNodePath);
-       }
-
-       @Override
-       public boolean equals(Object obj) {
-               if (!(obj instanceof JackrabbitDataModelMigration))
-                       return false;
-               JackrabbitDataModelMigration dataModelMigration = (JackrabbitDataModelMigration) obj;
-               return dataModelNodePath.equals(dataModelMigration.dataModelNodePath)
-                               && targetVersion.equals(dataModelMigration.targetVersion);
-       }
-
-       @Override
-       public int hashCode() {
-               return targetVersion.hashCode();
-       }
-
-       public void setDataModelNodePath(String dataModelNodePath) {
-               this.dataModelNodePath = dataModelNodePath;
-       }
-
-       public void setTargetVersion(String targetVersion) {
-               this.targetVersion = targetVersion;
-       }
-
-       public void setMigrationCnd(Resource migrationCnd) {
-               this.migrationCnd = migrationCnd;
-       }
-
-       public void setDataModification(JcrCallback dataModification) {
-               this.dataModification = dataModification;
-       }
-
-       public String getDataModelNodePath() {
-               return dataModelNodePath;
-       }
-
-       public String getTargetVersion() {
-               return targetVersion;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java
deleted file mode 100644 (file)
index e413488..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jackrabbit;
-
-import java.io.File;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.RepositoryFactory;
-import javax.jcr.Session;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.commons.JcrUtils;
-import org.apache.jackrabbit.core.RepositoryImpl;
-import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
-import org.apache.jackrabbit.jcr2dav.Jcr2davRepositoryFactory;
-import org.argeo.jcr.ArgeoJcrConstants;
-import org.argeo.jcr.ArgeoJcrException;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.Resource;
-import org.xml.sax.InputSource;
-
-/**
- * Repository factory which can create new repositories and access remote
- * Jackrabbit repositories
- */
-public class JackrabbitRepositoryFactory implements RepositoryFactory, ArgeoJcrConstants {
-       private final static Log log = LogFactory.getLog(JackrabbitRepositoryFactory.class);
-
-       private Resource fileRepositoryConfiguration = new ClassPathResource("/org/argeo/jackrabbit/repository-h2.xml");
-
-       @SuppressWarnings({ "rawtypes" })
-       public Repository getRepository(Map parameters) throws RepositoryException {
-               // // check if can be found by alias
-               // Repository repository = super.getRepository(parameters);
-               // if (repository != null)
-               // return repository;
-
-               // check if remote
-               Repository repository;
-               String uri = null;
-               if (parameters.containsKey(JCR_REPOSITORY_URI))
-                       uri = parameters.get(JCR_REPOSITORY_URI).toString();
-               else if (parameters.containsKey(JcrUtils.REPOSITORY_URI))
-                       uri = parameters.get(JcrUtils.REPOSITORY_URI).toString();
-
-               if (uri != null) {
-                       if (uri.startsWith("http"))// http, https
-                               repository = createRemoteRepository(uri);
-                       else if (uri.startsWith("file"))// http, https
-                               repository = createFileRepository(uri, parameters);
-                       else if (uri.startsWith("vm")) {
-                               log.warn("URI " + uri + " should have been managed by generic JCR repository factory");
-                               repository = getRepositoryByAlias(getAliasFromURI(uri));
-                       } else
-                               throw new ArgeoJcrException("Unrecognized URI format " + uri);
-
-               }
-
-               else if (parameters.containsKey(JCR_REPOSITORY_ALIAS)) {
-                       // Properties properties = new Properties();
-                       // properties.putAll(parameters);
-                       String alias = parameters.get(JCR_REPOSITORY_ALIAS).toString();
-                       // publish(alias, repository, properties);
-                       // log.info("Registered JCR repository under alias '" + alias + "'
-                       // with properties " + properties);
-                       repository = getRepositoryByAlias(alias);
-               } else
-                       throw new ArgeoJcrException("Not enough information in " + parameters);
-
-               if (repository == null)
-                       throw new ArgeoJcrException("Repository not found " + parameters);
-
-               return repository;
-       }
-
-       protected Repository getRepositoryByAlias(String alias) {
-               return null;
-       }
-
-       protected Repository createRemoteRepository(String uri) throws RepositoryException {
-               Map<String, String> params = new HashMap<String, String>();
-               params.put(JcrUtils.REPOSITORY_URI, uri);
-               Repository repository = new Jcr2davRepositoryFactory().getRepository(params);
-               if (repository == null)
-                       throw new ArgeoJcrException("Remote Davex repository " + uri + " not found");
-               log.info("Initialized remote Jackrabbit repository from uri " + uri);
-               return repository;
-       }
-
-       @SuppressWarnings({ "rawtypes", "unchecked" })
-       protected Repository createFileRepository(final String uri, Map parameters) throws RepositoryException {
-               InputStream configurationIn = null;
-               try {
-                       Properties vars = new Properties();
-                       vars.putAll(parameters);
-                       String dirPath = uri.substring("file:".length());
-                       File homeDir = new File(dirPath);
-                       if (homeDir.exists() && !homeDir.isDirectory())
-                               throw new ArgeoJcrException("Repository home " + dirPath + " is not a directory");
-                       if (!homeDir.exists())
-                               homeDir.mkdirs();
-                       configurationIn = fileRepositoryConfiguration.getInputStream();
-                       vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE, homeDir.getCanonicalPath());
-                       RepositoryConfig repositoryConfig = RepositoryConfig.create(new InputSource(configurationIn), vars);
-
-                       // TransientRepository repository = new
-                       // TransientRepository(repositoryConfig);
-                       final RepositoryImpl repository = RepositoryImpl.create(repositoryConfig);
-                       Session session = repository.login();
-                       // FIXME make it generic
-                       org.argeo.jcr.JcrUtils.addPrivilege(session, "/", "ROLE_ADMIN", "jcr:all");
-                       org.argeo.jcr.JcrUtils.logoutQuietly(session);
-                       Runtime.getRuntime().addShutdownHook(new Thread("Clean JCR repository " + uri) {
-                               public void run() {
-                                       repository.shutdown();
-                                       log.info("Destroyed repository " + uri);
-                               }
-                       });
-                       log.info("Initialized file Jackrabbit repository from uri " + uri);
-                       return repository;
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot create repository " + uri, e);
-               } finally {
-                       IOUtils.closeQuietly(configurationIn);
-               }
-       }
-
-       protected String getAliasFromURI(String uri) {
-               try {
-                       URI uriObj = new URI(uri);
-                       String alias = uriObj.getPath();
-                       if (alias.charAt(0) == '/')
-                               alias = alias.substring(1);
-                       if (alias.charAt(alias.length() - 1) == '/')
-                               alias = alias.substring(0, alias.length() - 1);
-                       return alias;
-               } catch (URISyntaxException e) {
-                       throw new ArgeoJcrException("Cannot interpret URI " + uri, e);
-               }
-       }
-
-       /**
-        * Called after the repository has been initialised. Does nothing by
-        * default.
-        */
-       @SuppressWarnings("rawtypes")
-       protected void postInitialization(Repository repository, Map parameters) {
-
-       }
-
-       public void setFileRepositoryConfiguration(Resource fileRepositoryConfiguration) {
-               this.fileRepositoryConfiguration = fileRepositoryConfiguration;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitWrapper.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitWrapper.java
deleted file mode 100644 (file)
index 268ecdb..0000000
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jackrabbit;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.jcr.Credentials;
-import javax.jcr.LoginException;
-import javax.jcr.NoSuchWorkspaceException;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.nodetype.NodeType;
-
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.api.JackrabbitRepository;
-import org.apache.jackrabbit.commons.NamespaceHelper;
-import org.apache.jackrabbit.commons.cnd.CndImporter;
-import org.argeo.jcr.ArgeoJcrConstants;
-import org.argeo.jcr.ArgeoJcrException;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.ArgeoTypes;
-import org.argeo.jcr.JcrRepositoryWrapper;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.util.security.DigestUtils;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.packageadmin.ExportedPackage;
-import org.osgi.service.packageadmin.PackageAdmin;
-import org.springframework.context.ResourceLoaderAware;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.ResourceLoader;
-
-/**
- * Wrapper around a Jackrabbit repository which allows to simplify configuration
- * and intercept some actions. It exposes itself as a {@link Repository}.
- */
-@Deprecated
-public class JackrabbitWrapper extends JcrRepositoryWrapper implements
-               JackrabbitRepository, ResourceLoaderAware {
-       private final static Log log = LogFactory.getLog(JackrabbitWrapper.class);
-       private final static String DIGEST_ALGORITHM = "MD5";
-
-       // local
-       private ResourceLoader resourceLoader;
-
-       // data model
-       /** Node type definitions in CND format */
-       private List<String> cndFiles = new ArrayList<String>();
-       /**
-        * Always import CNDs. Useful during development of new data models. In
-        * production, explicit migration processes should be used.
-        */
-       private Boolean forceCndImport = true;
-
-       /** Namespaces to register: key is prefix, value namespace */
-       private Map<String, String> namespaces = new HashMap<String, String>();
-
-       private BundleContext bundleContext;
-
-       /**
-        * Explicitly set admin credentials used in initialization. Useful for
-        * testing, in real applications authentication is rather dealt with
-        * externally
-        */
-       private Credentials adminCredentials = null;
-
-       /**
-        * Empty constructor, {@link #init()} should be called after properties have
-        * been set
-        */
-       public JackrabbitWrapper() {
-       }
-
-       @Override
-       public void init() {
-               prepareDataModel();
-       }
-
-       /*
-        * DATA MODEL
-        */
-
-       /**
-        * Import declared node type definitions and register namespaces. Tries to
-        * update the node definitions if they have changed. In case of failures an
-        * error will be logged but no exception will be thrown.
-        */
-       protected void prepareDataModel() {
-               if ((cndFiles == null || cndFiles.size() == 0)
-                               && (namespaces == null || namespaces.size() == 0))
-                       return;
-
-               Session session = null;
-               try {
-                       session = login(adminCredentials);
-                       // register namespaces
-                       if (namespaces.size() > 0) {
-                               NamespaceHelper namespaceHelper = new NamespaceHelper(session);
-                               namespaceHelper.registerNamespaces(namespaces);
-                       }
-
-                       // load CND files from classpath or as URL
-                       for (String resUrl : cndFiles) {
-                               processCndFile(session, resUrl);
-                       }
-               } catch (Exception e) {
-                       JcrUtils.discardQuietly(session);
-                       throw new ArgeoJcrException("Cannot import node type definitions "
-                                       + cndFiles, e);
-               } finally {
-                       JcrUtils.logoutQuietly(session);
-               }
-
-       }
-
-       protected void processCndFile(Session session, String resUrl) {
-               Reader reader = null;
-               try {
-                       // check existing data model nodes
-                       new NamespaceHelper(session).registerNamespace(ArgeoNames.ARGEO,
-                                       ArgeoNames.ARGEO_NAMESPACE);
-                       if (!session.itemExists(ArgeoJcrConstants.DATA_MODELS_BASE_PATH))
-                               JcrUtils.mkdirs(session,
-                                               ArgeoJcrConstants.DATA_MODELS_BASE_PATH);
-                       Node dataModels = session
-                                       .getNode(ArgeoJcrConstants.DATA_MODELS_BASE_PATH);
-                       NodeIterator it = dataModels.getNodes();
-                       Node dataModel = null;
-                       while (it.hasNext()) {
-                               Node node = it.nextNode();
-                               if (node.getProperty(ArgeoNames.ARGEO_URI).getString()
-                                               .equals(resUrl)) {
-                                       dataModel = node;
-                                       break;
-                               }
-                       }
-
-                       byte[] cndContent = readCndContent(resUrl);
-                       String newDigest = DigestUtils.digest(DIGEST_ALGORITHM, cndContent);
-                       Bundle bundle = findDataModelBundle(resUrl);
-
-                       String currentVersion = null;
-                       if (dataModel != null) {
-                               currentVersion = dataModel.getProperty(
-                                               ArgeoNames.ARGEO_DATA_MODEL_VERSION).getString();
-                               if (dataModel.hasNode(Node.JCR_CONTENT)) {
-                                       String oldDigest = JcrUtils.checksumFile(dataModel,
-                                                       DIGEST_ALGORITHM);
-                                       if (oldDigest.equals(newDigest)) {
-                                               if (log.isTraceEnabled())
-                                                       log.trace("Data model " + resUrl
-                                                                       + " hasn't changed, keeping version "
-                                                                       + currentVersion);
-                                               return;
-                                       }
-                               }
-                       }
-
-                       if (dataModel != null && !forceCndImport) {
-                               log.info("Data model "
-                                               + resUrl
-                                               + " has changed since version "
-                                               + currentVersion
-                                               + (bundle != null ? ": version " + bundle.getVersion()
-                                                               + ", bundle " + bundle.getSymbolicName() : ""));
-                               return;
-                       }
-
-                       reader = new InputStreamReader(new ByteArrayInputStream(cndContent));
-                       // actually imports the CND
-                       try {
-                               CndImporter.registerNodeTypes(reader, session, true);
-                       } catch (Exception e) {
-                               log.error("Cannot import data model " + resUrl, e);
-                               return;
-                       }
-
-                       if (dataModel != null && !dataModel.isNodeType(NodeType.NT_FILE)) {
-                               dataModel.remove();
-                               dataModel = null;
-                       }
-
-                       // FIXME: what if argeo.cnd would not be the first called on
-                       // a new repo? argeo:dataModel would not be found
-                       String fileName = FilenameUtils.getName(resUrl);
-                       if (dataModel == null) {
-                               dataModel = dataModels.addNode(fileName, NodeType.NT_FILE);
-                               dataModel.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
-                               dataModel.addMixin(ArgeoTypes.ARGEO_DATA_MODEL);
-                               dataModel.setProperty(ArgeoNames.ARGEO_URI, resUrl);
-                       } else {
-                               session.getWorkspace().getVersionManager()
-                                               .checkout(dataModel.getPath());
-                       }
-                       if (bundle != null)
-                               dataModel.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION,
-                                               bundle.getVersion().toString());
-                       else
-                               dataModel.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION,
-                                               "0.0.0");
-                       JcrUtils.copyBytesAsFile(dataModel.getParent(), fileName,
-                                       cndContent);
-                       JcrUtils.updateLastModified(dataModel);
-                       session.save();
-                       session.getWorkspace().getVersionManager()
-                                       .checkin(dataModel.getPath());
-
-                       if (currentVersion == null)
-                               log.info("Data model "
-                                               + resUrl
-                                               + (bundle != null ? ", version " + bundle.getVersion()
-                                                               + ", bundle " + bundle.getSymbolicName() : ""));
-                       else
-                               log.info("Data model "
-                                               + resUrl
-                                               + " updated from version "
-                                               + currentVersion
-                                               + (bundle != null ? ", version " + bundle.getVersion()
-                                                               + ", bundle " + bundle.getSymbolicName() : ""));
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot process data model " + resUrl, e);
-               } finally {
-                       IOUtils.closeQuietly(reader);
-               }
-       }
-
-       protected byte[] readCndContent(String resUrl) {
-               InputStream in = null;
-               try {
-                       boolean classpath;
-                       // normalize URL
-                       if (bundleContext != null && resUrl.startsWith("classpath:")) {
-                               resUrl = resUrl.substring("classpath:".length());
-                               classpath = true;
-                       } else if (resUrl.indexOf(':') < 0) {
-                               if (!resUrl.startsWith("/")) {
-                                       resUrl = "/" + resUrl;
-                                       log.warn("Classpath should start with '/'");
-                               }
-                               classpath = true;
-                       } else {
-                               classpath = false;
-                       }
-
-                       URL url = null;
-                       if (classpath) {
-                               if (bundleContext != null) {
-                                       Bundle currentBundle = bundleContext.getBundle();
-                                       url = currentBundle.getResource(resUrl);
-                               } else {
-                                       resUrl = "classpath:" + resUrl;
-                                       url = null;
-                               }
-                       } else if (!resUrl.startsWith("classpath:")) {
-                               url = new URL(resUrl);
-                       }
-
-                       if (url != null) {
-                               in = url.openStream();
-                       } else if (resourceLoader != null) {
-                               Resource res = resourceLoader.getResource(resUrl);
-                               in = res.getInputStream();
-                               url = res.getURL();
-                       } else {
-                               throw new ArgeoJcrException("No " + resUrl + " in the classpath,"
-                                               + " make sure the containing" + " package is visible.");
-                       }
-
-                       return IOUtils.toByteArray(in);
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot read CND from " + resUrl, e);
-               } finally {
-                       IOUtils.closeQuietly(in);
-               }
-       }
-
-       /*
-        * JACKRABBIT REPOSITORY IMPLEMENTATION
-        */
-       @Override
-       public Session login(Credentials credentials, String workspaceName,
-                       Map<String, Object> attributes) throws LoginException,
-                       NoSuchWorkspaceException, RepositoryException {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public void shutdown() {
-               // TODO Auto-generated method stub
-
-       }
-
-       /*
-        * UTILITIES
-        */
-       /** Find which OSGi bundle provided the data model resource */
-       protected Bundle findDataModelBundle(String resUrl) {
-               if (bundleContext == null)
-                       return null;
-
-               if (resUrl.startsWith("/"))
-                       resUrl = resUrl.substring(1);
-               String pkg = resUrl.substring(0, resUrl.lastIndexOf('/')).replace('/',
-                               '.');
-               ServiceReference<PackageAdmin> paSr = bundleContext
-                               .getServiceReference(PackageAdmin.class);
-               PackageAdmin packageAdmin = (PackageAdmin) bundleContext
-                               .getService(paSr);
-
-               // find exported package
-               ExportedPackage exportedPackage = null;
-               ExportedPackage[] exportedPackages = packageAdmin
-                               .getExportedPackages(pkg);
-               if (exportedPackages == null)
-                       throw new ArgeoJcrException("No exported package found for " + pkg);
-               for (ExportedPackage ep : exportedPackages) {
-                       for (Bundle b : ep.getImportingBundles()) {
-                               if (b.getBundleId() == bundleContext.getBundle().getBundleId()) {
-                                       exportedPackage = ep;
-                                       break;
-                               }
-                       }
-               }
-
-               Bundle exportingBundle = null;
-               if (exportedPackage != null) {
-                       exportingBundle = exportedPackage.getExportingBundle();
-               } else {
-                       // assume this is in the same bundle
-                       exportingBundle = bundleContext.getBundle();
-                       // throw new ArgeoJcrException("No OSGi exporting package found for "
-                       // + resUrl);
-               }
-               return exportingBundle;
-       }
-
-       /*
-        * FIELDS ACCESS
-        */
-       public void setNamespaces(Map<String, String> namespaces) {
-               this.namespaces = namespaces;
-       }
-
-       public void setCndFiles(List<String> cndFiles) {
-               this.cndFiles = cndFiles;
-       }
-
-       public void setBundleContext(BundleContext bundleContext) {
-               this.bundleContext = bundleContext;
-       }
-
-       protected BundleContext getBundleContext() {
-               return bundleContext;
-       }
-
-       public void setForceCndImport(Boolean forceCndUpdate) {
-               this.forceCndImport = forceCndUpdate;
-       }
-
-       public void setResourceLoader(ResourceLoader resourceLoader) {
-               this.resourceLoader = resourceLoader;
-       }
-
-       public void setAdminCredentials(Credentials adminCredentials) {
-               this.adminCredentials = adminCredentials;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-h2.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-h2.xml
deleted file mode 100644 (file)
index 0526762..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Repository PUBLIC "Jackrabbit 2.6" "http://jackrabbit.apache.org/dtd/repository-2.6.dtd">
-<Repository>
-       <!-- Shared datasource -->
-       <DataSources>
-               <DataSource name="dataSource">
-                       <param name="driver" value="org.h2.Driver" />
-                       <param name="url" value="${dburl}" />
-                       <param name="user" value="${dbuser}" />
-                       <param name="password" value="${dbpassword}" />
-                       <param name="databaseType" value="h2" />
-                       <param name="maxPoolSize" value="${maxPoolSize}" />
-               </DataSource>
-       </DataSources>
-
-       <!-- File system and datastore -->
-       <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
-               <param name="dataSourceName" value="dataSource" />
-               <param name="schema" value="default" />
-               <param name="schemaObjectPrefix" value="fs_" />
-       </FileSystem>
-       <DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
-               <param name="path" value="${rep.home}/datastore" />
-       </DataStore>
-
-       <!-- Workspace templates -->
-       <Workspaces rootPath="${rep.home}/workspaces"
-               defaultWorkspace="${defaultWorkspace}" />
-       <Workspace name="${wsp.name}">
-               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schema" value="default" />
-                       <param name="schemaObjectPrefix" value="${wsp.name}_fs_" />
-               </FileSystem>
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schemaObjectPrefix" value="${wsp.name}_pm_" />
-                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
-               </PersistenceManager>
-               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-                       <param name="path" value="${wsp.home}/index" />
-                       <param name="extractorPoolSize" value="${extractorPoolSize}" />
-                       <param name="cacheSize" value="${searchCacheSize}" />
-                       <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
-               </SearchIndex>
-               <WorkspaceSecurity>
-                       <AccessControlProvider
-                               class="org.argeo.security.jackrabbit.ArgeoAccessControlProvider" />
-               </WorkspaceSecurity>
-       </Workspace>
-
-       <!-- Versioning -->
-       <Versioning rootPath="${rep.home}/version">
-               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schema" value="default" />
-                       <param name="schemaObjectPrefix" value="fs_ver_" />
-               </FileSystem>
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schemaObjectPrefix" value="pm_ver_" />
-                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
-               </PersistenceManager>
-       </Versioning>
-
-       <!-- Indexing -->
-       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-               <param name="path" value="${rep.home}/index" />
-               <param name="extractorPoolSize" value="${extractorPoolSize}" />
-               <param name="cacheSize" value="${searchCacheSize}" />
-               <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
-       </SearchIndex>
-
-       <!-- Security -->
-       <Security appName="Jackrabbit">
-               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
-                       workspaceName="security" />
-               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager" />
-       </Security>
-</Repository>
\ No newline at end of file
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-localfs.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-localfs.xml
deleted file mode 100644 (file)
index 3d24708..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE Repository PUBLIC "Jackrabbit 2.6" "http://jackrabbit.apache.org/dtd/repository-2.6.dtd">
-<Repository>
-       <!-- File system and datastore -->
-       <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
-               <param name="path" value="${rep.home}/repository" />
-       </FileSystem>
-       <DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
-               <param name="path" value="${rep.home}/datastore" />
-       </DataStore>
-
-       <!-- Workspace templates -->
-       <Workspaces rootPath="${rep.home}/workspaces"
-               defaultWorkspace="${defaultWorkspace}" />
-       <Workspace name="${wsp.name}">
-               <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
-                       <param name="path" value="${wsp.home}" />
-               </FileSystem>
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
-                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
-               </PersistenceManager>
-               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-                       <param name="path" value="${wsp.home}/index" />
-                       <param name="extractorPoolSize" value="${extractorPoolSize}" />
-                       <param name="cacheSize" value="${searchCacheSize}" />
-                       <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
-               </SearchIndex>
-               <WorkspaceSecurity>
-                       <AccessControlProvider
-                               class="org.argeo.security.jackrabbit.ArgeoAccessControlProvider" />
-               </WorkspaceSecurity>
-       </Workspace>
-
-       <!-- Versioning -->
-       <Versioning rootPath="${rep.home}/version">
-               <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
-                       <param name="path" value="${rep.home}/version" />
-               </FileSystem>
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
-                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
-               </PersistenceManager>
-       </Versioning>
-
-       <!-- Indexing -->
-       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-               <param name="path" value="${rep.home}/index" />
-               <param name="extractorPoolSize" value="${extractorPoolSize}" />
-               <param name="cacheSize" value="${searchCacheSize}" />
-               <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
-       </SearchIndex>
-
-       <!-- Security -->
-       <Security appName="Jackrabbit">
-               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
-                       workspaceName="security" />
-               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager" />
-       </Security>
-</Repository>
\ No newline at end of file
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-memory.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-memory.xml
deleted file mode 100644 (file)
index ecee5bd..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE Repository PUBLIC "Jackrabbit 2.6" "http://jackrabbit.apache.org/dtd/repository-2.6.dtd">
-<Repository>
-       <!-- File system and datastore -->
-       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-
-       <!-- Workspace templates -->
-       <Workspaces rootPath="${rep.home}/workspaces"
-               defaultWorkspace="${defaultWorkspace}" configRootPath="/workspaces" />
-       <Workspace name="${wsp.name}">
-               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
-                       <param name="blobFSBlockSize" value="1" />
-                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
-               </PersistenceManager>
-               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-                       <param name="path" value="${wsp.home}/index" />
-                       <param name="directoryManagerClass"
-                               value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
-                       <param name="extractorPoolSize" value="${extractorPoolSize}" />
-                       <param name="cacheSize" value="${searchCacheSize}" />
-                       <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
-                       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-               </SearchIndex>
-       </Workspace>
-
-       <!-- Versioning -->
-       <Versioning rootPath="${rep.home}/version">
-               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
-                       <param name="blobFSBlockSize" value="1" />
-                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
-               </PersistenceManager>
-       </Versioning>
-
-       <!-- Indexing -->
-       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-               <param name="path" value="${rep.home}/index" />
-               <param name="directoryManagerClass"
-                       value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
-               <param name="extractorPoolSize" value="${extractorPoolSize}" />
-               <param name="cacheSize" value="${searchCacheSize}" />
-               <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
-               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-       </SearchIndex>
-
-       <!-- Security -->
-       <Security appName="Jackrabbit">
-               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
-                       workspaceName="security" />
-               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager" />
-       </Security>
-</Repository>
\ No newline at end of file
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml
deleted file mode 100644 (file)
index 07a0d04..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE Repository PUBLIC "Jackrabbit 2.6" "http://jackrabbit.apache.org/dtd/repository-2.6.dtd">
-<Repository>
-       <!-- Shared datasource -->
-       <DataSources>
-               <DataSource name="dataSource">
-                       <param name="driver" value="org.postgresql.Driver" />
-                       <param name="url" value="${dburl}" />
-                       <param name="user" value="${dbuser}" />
-                       <param name="password" value="${dbpassword}" />
-                       <param name="databaseType" value="postgresql" />
-                       <param name="maxPoolSize" value="${maxPoolSize}" />
-               </DataSource>
-       </DataSources>
-
-       <!-- File system and datastore -->
-       <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
-               <param name="dataSourceName" value="dataSource" />
-               <param name="schema" value="postgresql" />
-               <param name="schemaObjectPrefix" value="fs_" />
-       </FileSystem>
-       <DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
-               <param name="path" value="${rep.home}/datastore" />
-       </DataStore>
-
-       <!-- Workspace templates -->
-       <Workspaces rootPath="${rep.home}/workspaces"
-               defaultWorkspace="${defaultWorkspace}" />
-       <Workspace name="${wsp.name}">
-               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schema" value="postgresql" />
-                       <param name="schemaObjectPrefix" value="${wsp.name}_fs_" />
-               </FileSystem>
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.pool.PostgreSQLPersistenceManager">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schemaObjectPrefix" value="${wsp.name}_pm_" />
-                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
-               </PersistenceManager>
-               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-                       <param name="path" value="${wsp.home}/index" />
-                       <param name="extractorPoolSize" value="${extractorPoolSize}" />
-                       <param name="cacheSize" value="${searchCacheSize}" />
-                       <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
-               </SearchIndex>
-               <WorkspaceSecurity>
-                       <AccessControlProvider
-                               class="org.argeo.security.jackrabbit.ArgeoAccessControlProvider" />
-               </WorkspaceSecurity>
-       </Workspace>
-
-       <!-- Versioning -->
-       <Versioning rootPath="${rep.home}/version">
-               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schema" value="postgresql" />
-                       <param name="schemaObjectPrefix" value="fs_ver_" />
-               </FileSystem>
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.pool.PostgreSQLPersistenceManager">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schemaObjectPrefix" value="pm_ver_" />
-                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
-               </PersistenceManager>
-       </Versioning>
-
-       <!-- Indexing -->
-       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-               <param name="path" value="${rep.home}/index" />
-               <param name="extractorPoolSize" value="${extractorPoolSize}" />
-               <param name="cacheSize" value="${searchCacheSize}" />
-               <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
-       </SearchIndex>
-
-       <!-- Security -->
-       <Security appName="Jackrabbit">
-               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
-                       workspaceName="security" />
-               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager" />
-       </Security>
-</Repository>
\ No newline at end of file
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml
deleted file mode 100644 (file)
index 9677828..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE Repository PUBLIC "Jackrabbit 2.6" "http://jackrabbit.apache.org/dtd/repository-2.6.dtd">
-<Repository>
-       <!-- Shared datasource -->
-       <DataSources>
-               <DataSource name="dataSource">
-                       <param name="driver" value="org.postgresql.Driver" />
-                       <param name="url" value="${dburl}" />
-                       <param name="user" value="${dbuser}" />
-                       <param name="password" value="${dbpassword}" />
-                       <param name="databaseType" value="postgresql" />
-                       <param name="maxPoolSize" value="${maxPoolSize}" />
-               </DataSource>
-       </DataSources>
-
-       <!-- File system and datastore -->
-       <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
-               <param name="dataSourceName" value="dataSource" />
-               <param name="schema" value="postgresql" />
-               <param name="schemaObjectPrefix" value="fs_" />
-       </FileSystem>
-
-       <!-- Workspace templates -->
-       <Workspaces rootPath="${rep.home}/workspaces"
-               defaultWorkspace="${defaultWorkspace}" />
-       <Workspace name="${wsp.name}">
-               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schema" value="postgresql" />
-                       <param name="schemaObjectPrefix" value="${wsp.name}_fs_" />
-               </FileSystem>
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.pool.PostgreSQLPersistenceManager">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schemaObjectPrefix" value="${wsp.name}_pm_" />
-                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
-               </PersistenceManager>
-               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-                       <param name="path" value="${wsp.home}/index" />
-                       <param name="extractorPoolSize" value="${extractorPoolSize}" />
-                       <param name="cacheSize" value="${searchCacheSize}" />
-                       <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
-               </SearchIndex>
-               <WorkspaceSecurity>
-                       <AccessControlProvider
-                               class="org.argeo.security.jackrabbit.ArgeoAccessControlProvider" />
-               </WorkspaceSecurity>
-       </Workspace>
-
-       <!-- Versioning -->
-       <Versioning rootPath="${rep.home}/version">
-               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schema" value="postgresql" />
-                       <param name="schemaObjectPrefix" value="fs_ver_" />
-               </FileSystem>
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.pool.PostgreSQLPersistenceManager">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schemaObjectPrefix" value="pm_ver_" />
-                       <param name="bundleCacheSize" value="${bundleCacheMB}" />
-               </PersistenceManager>
-       </Versioning>
-
-       <!-- Indexing -->
-       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-               <param name="path" value="${rep.home}/index" />
-               <param name="extractorPoolSize" value="${extractorPoolSize}" />
-               <param name="cacheSize" value="${searchCacheSize}" />
-               <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
-       </SearchIndex>
-
-       <!-- Security -->
-       <Security appName="Jackrabbit">
-               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
-                       workspaceName="security" />
-               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager" />
-       </Security>
-</Repository>
\ No newline at end of file
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/OpenInViewSessionProvider.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/OpenInViewSessionProvider.java
deleted file mode 100644 (file)
index 52a9883..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jackrabbit.servlet;
-
-import java.io.Serializable;
-
-import javax.jcr.LoginException;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.server.SessionProvider;
-import org.argeo.jcr.JcrUtils;
-
-/**
- * Implements an open session in view patter: a new JCR session is created for
- * each request
- */
-@Deprecated
-public class OpenInViewSessionProvider implements SessionProvider, Serializable {
-       private static final long serialVersionUID = 2270957712453841368L;
-
-       private final static Log log = LogFactory
-                       .getLog(OpenInViewSessionProvider.class);
-
-       public Session getSession(HttpServletRequest request, Repository rep,
-                       String workspace) throws LoginException, ServletException,
-                       RepositoryException {
-               return login(request, rep, workspace);
-       }
-
-       protected Session login(HttpServletRequest request, Repository repository,
-                       String workspace) throws RepositoryException {
-               if (log.isTraceEnabled())
-                       log.trace("Login to workspace "
-                                       + (workspace == null ? "<default>" : workspace)
-                                       + " in web session " + request.getSession().getId());
-               return repository.login(workspace);
-       }
-
-       public void releaseSession(Session session) {
-               JcrUtils.logoutQuietly(session);
-               if (log.isTraceEnabled())
-                       log.trace("Logged out remote JCR session " + session);
-       }
-
-       public void init() {
-       }
-
-       public void destroy() {
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/RemotingServlet.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/RemotingServlet.java
deleted file mode 100644 (file)
index 3fdb5d2..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jackrabbit.servlet;
-
-import javax.jcr.Repository;
-
-import org.apache.jackrabbit.server.SessionProvider;
-import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet;
-
-/** Provides remote access to a JCR repository */
-@Deprecated
-public class RemotingServlet extends JcrRemotingServlet {
-       public final static String INIT_PARAM_RESOURCE_PATH_PREFIX = JcrRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX;
-       public final static String INIT_PARAM_HOME = JcrRemotingServlet.INIT_PARAM_HOME;
-       public final static String INIT_PARAM_TMP_DIRECTORY = JcrRemotingServlet.INIT_PARAM_TMP_DIRECTORY;
-       public final static String INIT_PARAM_PROTECTED_HANDLERS_CONFIG = JcrRemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG;
-
-       private static final long serialVersionUID = 3131835511468341309L;
-
-       private final Repository repository;
-       private final SessionProvider sessionProvider;
-
-       public RemotingServlet(Repository repository,
-                       SessionProvider sessionProvider) {
-               this.repository = repository;
-               this.sessionProvider = sessionProvider;
-       }
-
-       @Override
-       protected Repository getRepository() {
-               return repository;
-       }
-
-       @Override
-       protected SessionProvider getSessionProvider() {
-               return sessionProvider;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/WebdavServlet.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/servlet/WebdavServlet.java
deleted file mode 100644 (file)
index e3176b7..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jackrabbit.servlet;
-
-import java.io.IOException;
-
-import javax.jcr.Repository;
-import javax.servlet.ServletException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.server.SessionProvider;
-import org.apache.jackrabbit.webdav.DavException;
-import org.apache.jackrabbit.webdav.DavResource;
-import org.apache.jackrabbit.webdav.WebdavRequest;
-import org.apache.jackrabbit.webdav.WebdavResponse;
-import org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet;
-
-/** WebDav servlet whose repository is injected */
-@Deprecated
-public class WebdavServlet extends SimpleWebdavServlet {
-       public final static String INIT_PARAM_RESOURCE_CONFIG = SimpleWebdavServlet.INIT_PARAM_RESOURCE_CONFIG;
-       public final static String INIT_PARAM_RESOURCE_PATH_PREFIX = SimpleWebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX;
-
-       private static final long serialVersionUID = -369787931175177080L;
-
-       private final static Log log = LogFactory.getLog(WebdavServlet.class);
-
-       private final Repository repository;
-
-       public WebdavServlet(Repository repository, SessionProvider sessionProvider) {
-               this.repository = repository;
-               setSessionProvider(sessionProvider);
-       }
-
-       public Repository getRepository() {
-               return repository;
-       }
-
-       @Override
-       protected boolean execute(WebdavRequest request, WebdavResponse response,
-                       int method, DavResource resource) throws ServletException,
-                       IOException, DavException {
-               if (log.isTraceEnabled())
-                       log.trace(request.getMethod() + "\t" + request.getPathInfo());
-               boolean res = super.execute(request, response, method, resource);
-               return res;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/AbstractJackrabbitTestCase.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/AbstractJackrabbitTestCase.java
deleted file mode 100644 (file)
index 1523c83..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jackrabbit.unit;
-
-import java.net.URL;
-
-import javax.jcr.Repository;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.jackrabbit.core.RepositoryImpl;
-import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.argeo.jcr.unit.AbstractJcrTestCase;
-
-/** Factorizes configuration of an in memory transient repository */
-public abstract class AbstractJackrabbitTestCase extends AbstractJcrTestCase {
-       protected RepositoryImpl repositoryImpl;
-
-       // protected File getRepositoryFile() throws Exception {
-       // Resource res = new ClassPathResource(
-       // "org/argeo/jackrabbit/unit/repository-memory.xml");
-       // return res.getFile();
-       // }
-
-       public AbstractJackrabbitTestCase() {
-               URL url = AbstractJackrabbitTestCase.class.getResource("jaas.config");
-               assert url != null;
-               System.setProperty("java.security.auth.login.config", url.toString());
-       }
-
-       protected Repository createRepository() throws Exception {
-               // Repository repository = new TransientRepository(getRepositoryFile(),
-               // getHomeDir());
-               RepositoryConfig repositoryConfig = RepositoryConfig.create(
-                               AbstractJackrabbitTestCase.class
-                                               .getResourceAsStream(getRepositoryConfigResource()),
-                               getHomeDir().getAbsolutePath());
-               RepositoryImpl repositoryImpl = RepositoryImpl.create(repositoryConfig);
-               return repositoryImpl;
-       }
-
-       protected String getRepositoryConfigResource() {
-               return "repository-memory.xml";
-       }
-
-       @Override
-       protected void clearRepository(Repository repository) throws Exception {
-               RepositoryImpl repositoryImpl = (RepositoryImpl) repository;
-               if (repositoryImpl != null)
-                       repositoryImpl.shutdown();
-               FileUtils.deleteDirectory(getHomeDir());
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/jaas.config b/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/jaas.config
deleted file mode 100644 (file)
index 11d2f96..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-TEST_JACKRABBIT_ADMIN {
-   org.argeo.jackrabbit.JackrabbitAdminLoginModule requisite;
-};
-
-Jackrabbit {
-   org.argeo.security.jackrabbit.SystemJackrabbitLoginModule requisite;
-};
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/repository-h2.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/repository-h2.xml
deleted file mode 100644 (file)
index 348dc28..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
-                            "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
-<Repository>
-       <!-- Shared datasource -->
-       <DataSources>
-               <DataSource name="dataSource">
-                       <param name="driver" value="org.h2.Driver" />
-                       <param name="url" value="jdbc:h2:mem:jackrabbit" />
-                       <param name="user" value="sa" />
-                       <param name="password" value="" />
-                       <param name="databaseType" value="h2" />
-                       <param name="maxPoolSize" value="10" />
-               </DataSource>
-       </DataSources>
-
-       <!-- File system and datastore -->
-       <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
-               <param name="dataSourceName" value="dataSource" />
-               <param name="schema" value="default" />
-               <param name="schemaObjectPrefix" value="fs_" />
-       </FileSystem>
-       <DataStore class="org.apache.jackrabbit.core.data.db.DbDataStore">
-               <param name="dataSourceName" value="dataSource" />
-               <param name="schemaObjectPrefix" value="ds_" />
-       </DataStore>
-
-       <!-- Workspace templates -->
-       <Workspaces rootPath="${rep.home}/workspaces"
-               defaultWorkspace="dev" />
-       <Workspace name="${wsp.name}">
-               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schema" value="default" />
-                       <param name="schemaObjectPrefix" value="${wsp.name}_fs_" />
-               </FileSystem>
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schemaObjectPrefix" value="${wsp.name}_pm_" />
-               </PersistenceManager>
-               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-                       <param name="path" value="${wsp.home}/index" />
-               </SearchIndex>
-       </Workspace>
-
-       <!-- Versioning -->
-       <Versioning rootPath="${rep.home}/version">
-               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schema" value="default" />
-                       <param name="schemaObjectPrefix" value="fs_ver_" />
-               </FileSystem>
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
-                       <param name="dataSourceName" value="dataSource" />
-                       <param name="schemaObjectPrefix" value="pm_ver_" />
-               </PersistenceManager>
-       </Versioning>
-
-       <!-- Indexing -->
-       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-               <param name="path" value="${rep.home}/repository/index" />
-               <param name="extractorPoolSize" value="2" />
-               <param name="supportHighlighting" value="true" />
-       </SearchIndex>
-
-       <!-- Security -->
-       <Security appName="Jackrabbit">
-               <SecurityManager
-                       class="org.apache.jackrabbit.core.security.simple.SimpleSecurityManager"
-                       workspaceName="security" />
-               <AccessManager
-                       class="org.apache.jackrabbit.core.security.simple.SimpleAccessManager" />
-               <LoginModule
-                       class="org.apache.jackrabbit.core.security.simple.SimpleLoginModule">
-                       <param name="anonymousId" value="anonymous" />
-                       <param name="adminId" value="admin" />
-               </LoginModule>
-       </Security>
-</Repository>
\ No newline at end of file
diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/repository-memory.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/unit/repository-memory.xml
deleted file mode 100644 (file)
index 8395424..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0"?>
-<!--
-
-    Copyright (C) 2007-2012 Argeo GmbH
-
-    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.
-
--->
-<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
-                            "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
-<Repository>
-       <!-- File system and datastore -->
-       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-
-       <!-- Workspace templates -->
-       <Workspaces rootPath="${rep.home}/workspaces"
-               defaultWorkspace="main" configRootPath="/workspaces" />
-       <Workspace name="${wsp.name}">
-               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
-                       <param name="blobFSBlockSize" value="1" />
-               </PersistenceManager>
-               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-                       <param name="path" value="${rep.home}/repository/index" />
-                       <param name="directoryManagerClass"
-                               value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
-                       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-               </SearchIndex>
-       </Workspace>
-
-       <!-- Versioning -->
-       <Versioning rootPath="${rep.home}/version">
-               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-               <PersistenceManager
-                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
-                       <param name="blobFSBlockSize" value="1" />
-               </PersistenceManager>
-       </Versioning>
-
-       <!-- Indexing -->
-       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-               <param name="path" value="${rep.home}/repository/index" />
-               <param name="directoryManagerClass"
-                       value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
-               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
-       </SearchIndex>
-
-       <!-- Security -->
-       <Security appName="Jackrabbit">
-               <SecurityManager
-                       class="org.apache.jackrabbit.core.security.simple.SimpleSecurityManager"
-                       workspaceName="security" />
-               <AccessManager
-                       class="org.apache.jackrabbit.core.security.simple.SimpleAccessManager" />
-               <LoginModule
-                       class="org.apache.jackrabbit.core.security.simple.SimpleLoginModule">
-                       <param name="anonymousId" value="anonymous" />
-                       <param name="adminId" value="admin" />
-               </LoginModule>
-       </Security>
-</Repository>
\ No newline at end of file
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java
deleted file mode 100644 (file)
index 26979e9..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import javax.jcr.Repository;
-
-/** Argeo model specific constants */
-public interface ArgeoJcrConstants {
-       public final static String ARGEO_BASE_PATH = "/argeo:system";
-       public final static String DATA_MODELS_BASE_PATH = ARGEO_BASE_PATH + "/argeo:dataModels";
-       public final static String PEOPLE_BASE_PATH = ARGEO_BASE_PATH + "/argeo:people";
-
-       // parameters (typically for call to a RepositoryFactory)
-       /** Key for a JCR repository alias */
-       public final static String JCR_REPOSITORY_ALIAS = "argeo.jcr.repository.alias";
-       /** Key for a JCR repository URI */
-       public final static String JCR_REPOSITORY_URI = "argeo.jcr.repository.uri";
-
-       // standard aliases
-       /**
-        * Reserved alias for the "node" {@link Repository}, that is, the default
-        * JCR repository.
-        */
-       public final static String ALIAS_NODE = "node";
-       public final static String ALIAS_HOME = "home";
-       public final static String BASE_REPO_PID = "argeo.repo.";
-       public final static String REPO_PID_NODE = BASE_REPO_PID + ALIAS_NODE;
-       public final static String JACKRABBIT_REPO_FACTORY_PID = "argeo.repo.factory.jackrabbit";
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrException.java b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrException.java
deleted file mode 100644 (file)
index 8e19593..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.argeo.jcr;
-
-/** Argeo JCR specific exceptions. */
-public class ArgeoJcrException extends RuntimeException {
-       private static final long serialVersionUID = -1941940005390084331L;
-
-       public ArgeoJcrException(String message, Throwable cause) {
-               super(message, cause);
-       }
-
-       public ArgeoJcrException(String message) {
-               super(message);
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrUtils.java b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrUtils.java
deleted file mode 100644 (file)
index 9a0fd19..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.RepositoryFactory;
-
-/** Utilities related to Argeo model in JCR */
-public class ArgeoJcrUtils implements ArgeoJcrConstants {
-       /**
-        * Wraps the call to the repository factory based on parameter
-        * {@link ArgeoJcrConstants#JCR_REPOSITORY_ALIAS} in order to simplify it
-        * and protect against future API changes.
-        */
-       public static Repository getRepositoryByAlias(
-                       RepositoryFactory repositoryFactory, String alias) {
-               try {
-                       Map<String, String> parameters = new HashMap<String, String>();
-                       parameters.put(JCR_REPOSITORY_ALIAS, alias);
-                       return repositoryFactory.getRepository(parameters);
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException(
-                                       "Unexpected exception when trying to retrieve repository with alias "
-                                                       + alias, e);
-               }
-       }
-
-       /**
-        * Wraps the call to the repository factory based on parameter
-        * {@link ArgeoJcrConstants#JCR_REPOSITORY_URI} in order to simplify it and
-        * protect against future API changes.
-        */
-       public static Repository getRepositoryByUri(
-                       RepositoryFactory repositoryFactory, String uri) {
-               return getRepositoryByUri(repositoryFactory, uri, null);
-       }
-
-       /**
-        * Wraps the call to the repository factory based on parameter
-        * {@link ArgeoJcrConstants#JCR_REPOSITORY_URI} in order to simplify it and
-        * protect against future API changes.
-        */
-       public static Repository getRepositoryByUri(
-                       RepositoryFactory repositoryFactory, String uri, String alias) {
-               try {
-                       Map<String, String> parameters = new HashMap<String, String>();
-                       parameters.put(JCR_REPOSITORY_URI, uri);
-                       if (alias != null)
-                               parameters.put(JCR_REPOSITORY_ALIAS, alias);
-                       return repositoryFactory.getRepository(parameters);
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException(
-                                       "Unexpected exception when trying to retrieve repository with uri "
-                                                       + uri, e);
-               }
-       }
-
-       private ArgeoJcrUtils() {
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoNames.java b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoNames.java
deleted file mode 100644 (file)
index 1d4582a..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-/** JCR names in the http://www.argeo.org/argeo namespace */
-public interface ArgeoNames {
-       public final static String ARGEO_NAMESPACE = "http://www.argeo.org/ns/argeo";
-       public final static String ARGEO = "argeo";
-
-       public final static String ARGEO_URI = "argeo:uri";
-       public final static String ARGEO_USER_ID = "argeo:userID";
-       public final static String ARGEO_PREFERENCES = "argeo:preferences";
-       public final static String ARGEO_DATA_MODEL_VERSION = "argeo:dataModelVersion";
-
-       public final static String ARGEO_REMOTE = "argeo:remote";
-       public final static String ARGEO_PASSWORD = "argeo:password";
-//     public final static String ARGEO_REMOTE_ROLES = "argeo:remoteRoles";
-
-       // user profile
-       public final static String ARGEO_PROFILE = "argeo:profile";
-
-       // spring security
-       public final static String ARGEO_ENABLED = "argeo:enabled";
-       public final static String ARGEO_ACCOUNT_NON_EXPIRED = "argeo:accountNonExpired";
-       public final static String ARGEO_ACCOUNT_NON_LOCKED = "argeo:accountNonLocked";
-       public final static String ARGEO_CREDENTIALS_NON_EXPIRED = "argeo:credentialsNonExpired";
-
-       // personal details
-       public final static String ARGEO_FIRST_NAME = "argeo:firstName";
-       public final static String ARGEO_LAST_NAME = "argeo:lastName";
-       public final static String ARGEO_PRIMARY_EMAIL = "argeo:primaryEmail";
-       public final static String ARGEO_PRIMARY_ORGANIZATION = "argeo:primaryOrganization";
-
-       // tabular
-       public final static String ARGEO_IS_KEY = "argeo:isKey";
-
-       // crypto
-       public final static String ARGEO_IV = "argeo:iv";
-       public final static String ARGEO_SECRET_KEY_FACTORY = "argeo:secretKeyFactory";
-       public final static String ARGEO_SALT = "argeo:salt";
-       public final static String ARGEO_ITERATION_COUNT = "argeo:iterationCount";
-       public final static String ARGEO_KEY_LENGTH = "argeo:keyLength";
-       public final static String ARGEO_SECRET_KEY_ENCRYPTION = "argeo:secretKeyEncryption";
-       public final static String ARGEO_CIPHER = "argeo:cipher";
-       public final static String ARGEO_KEYRING = "argeo:keyring";
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoTypes.java b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoTypes.java
deleted file mode 100644 (file)
index a11ead5..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-/** JCR types in the http://www.argeo.org/argeo namespace */
-public interface ArgeoTypes {
-       public final static String ARGEO_LINK = "argeo:link";
-       public final static String ARGEO_USER_HOME = "argeo:userHome";
-       public final static String ARGEO_USER_PROFILE = "argeo:userProfile";
-       public final static String ARGEO_REMOTE_REPOSITORY = "argeo:remoteRepository";
-       public final static String ARGEO_PREFERENCE_NODE = "argeo:preferenceNode";
-
-       // data model
-       public final static String ARGEO_DATA_MODEL = "argeo:dataModel";
-       
-       // tabular
-       public final static String ARGEO_TABLE = "argeo:table";
-       public final static String ARGEO_COLUMN = "argeo:column";
-       public final static String ARGEO_CSV = "argeo:csv";
-
-       // crypto
-       public final static String ARGEO_ENCRYPTED = "argeo:encrypted";
-       public final static String ARGEO_PBE_SPEC = "argeo:pbeSpec";
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/CollectionNodeIterator.java b/org.argeo.server.jcr/src/org/argeo/jcr/CollectionNodeIterator.java
deleted file mode 100644 (file)
index a65907a..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-
-/** Wraps a collection of nodes in order to read it as a {@link NodeIterator} */
-public class CollectionNodeIterator implements NodeIterator {
-       private final Long collectionSize;
-       private final Iterator<Node> iterator;
-       private Integer position = 0;
-
-       public CollectionNodeIterator(Collection<Node> nodes) {
-               super();
-               this.collectionSize = (long) nodes.size();
-               this.iterator = nodes.iterator();
-       }
-
-       public void skip(long skipNum) {
-               if (skipNum < 0)
-                       throw new IllegalArgumentException(
-                                       "Skip count has to be positive: " + skipNum);
-
-               for (long i = 0; i < skipNum; i++) {
-                       if (!hasNext())
-                               throw new NoSuchElementException("Last element past (position="
-                                               + getPosition() + ")");
-                       nextNode();
-               }
-       }
-
-       public long getSize() {
-               return collectionSize;
-       }
-
-       public long getPosition() {
-               return position;
-       }
-
-       public boolean hasNext() {
-               return iterator.hasNext();
-       }
-
-       public Object next() {
-               return nextNode();
-       }
-
-       public void remove() {
-               iterator.remove();
-       }
-
-       public Node nextNode() {
-               Node node = iterator.next();
-               position++;
-               return node;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/DefaultJcrListener.java b/org.argeo.server.jcr/src/org/argeo/jcr/DefaultJcrListener.java
deleted file mode 100644 (file)
index 5ef8edd..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.observation.Event;
-import javax.jcr.observation.EventIterator;
-import javax.jcr.observation.EventListener;
-import javax.jcr.observation.ObservationManager;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/** To be overridden */
-public class DefaultJcrListener implements EventListener {
-       private final static Log log = LogFactory.getLog(DefaultJcrListener.class);
-       private Session session;
-       private String path = "/";
-       private Boolean deep = true;
-
-       public void start() {
-               try {
-                       addEventListener(session().getWorkspace().getObservationManager());
-                       if (log.isDebugEnabled())
-                               log.debug("Registered JCR event listener on " + path);
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot register event listener", e);
-               }
-       }
-
-       public void stop() {
-               try {
-                       session().getWorkspace().getObservationManager()
-                                       .removeEventListener(this);
-                       if (log.isDebugEnabled())
-                               log.debug("Unregistered JCR event listener on " + path);
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot unregister event listener", e);
-               }
-       }
-
-       /** Default is listen to all events */
-       protected Integer getEvents() {
-               return Event.NODE_ADDED | Event.NODE_REMOVED | Event.PROPERTY_ADDED
-                               | Event.PROPERTY_CHANGED | Event.PROPERTY_REMOVED;
-       }
-
-       /** To be overidden */
-       public void onEvent(EventIterator events) {
-               while (events.hasNext()) {
-                       Event event = events.nextEvent();
-                       log.debug(event);
-               }
-       }
-
-       /** To be overidden */
-       protected void addEventListener(ObservationManager observationManager)
-                       throws RepositoryException {
-               observationManager.addEventListener(this, getEvents(), path, deep,
-                               null, null, false);
-       }
-
-       private Session session() {
-               return session;
-       }
-
-       public void setPath(String path) {
-               this.path = path;
-       }
-
-       public void setDeep(Boolean deep) {
-               this.deep = deep;
-       }
-
-       public void setSession(Session session) {
-               this.session = session;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/DefaultRepositoryFactory.java b/org.argeo.server.jcr/src/org/argeo/jcr/DefaultRepositoryFactory.java
deleted file mode 100644 (file)
index ccfc269..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.RepositoryFactory;
-
-/**
- * Simple implementation of {@link RepositoryFactory}, supporting OSGi aliases.
- */
-@Deprecated
-public class DefaultRepositoryFactory extends DefaultRepositoryRegister
-               implements RepositoryFactory, ArgeoJcrConstants {
-       @SuppressWarnings("rawtypes")
-       public Repository getRepository(Map parameters) throws RepositoryException {
-               if (parameters.containsKey(JCR_REPOSITORY_ALIAS)) {
-                       String alias = parameters.get(JCR_REPOSITORY_ALIAS).toString();
-                       return getRepositoryByAlias(alias);
-               } else if (parameters.containsKey(JCR_REPOSITORY_URI)) {
-                       String uri = parameters.get(JCR_REPOSITORY_URI).toString();
-                       return getRepositoryByAlias(getAliasFromURI(uri));
-               }
-               return null;
-       }
-
-       protected String getAliasFromURI(String uri) {
-               try {
-                       URI uriObj = new URI(uri);
-                       String alias = uriObj.getPath();
-                       if (alias.charAt(0) == '/')
-                               alias = alias.substring(1);
-                       if (alias.charAt(alias.length() - 1) == '/')
-                               alias = alias.substring(0, alias.length() - 1);
-                       return alias;
-               } catch (URISyntaxException e) {
-                       throw new ArgeoJcrException("Cannot interpret URI " + uri, e);
-               }
-       }
-
-       /**
-        * Retrieve a repository by alias
-        * 
-        * @return the repository registered with alias or null if none
-        */
-       protected Repository getRepositoryByAlias(String alias) {
-               if (getRepositories().containsKey(alias))
-                       return getRepositories().get(alias);
-               else
-                       return null;
-       }
-
-       protected void publish(String alias, Repository repository,
-                       Properties properties) {
-               register(repository, properties);
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/DefaultRepositoryRegister.java b/org.argeo.server.jcr/src/org/argeo/jcr/DefaultRepositoryRegister.java
deleted file mode 100644 (file)
index e2a5026..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Observable;
-import java.util.TreeMap;
-
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-@Deprecated
-public class DefaultRepositoryRegister extends Observable implements
-               RepositoryRegister, ArgeoJcrConstants {
-       private final static Log log = LogFactory
-                       .getLog(DefaultRepositoryRegister.class);
-
-       /** Read only map which will be directly exposed. */
-       private Map<String, Repository> repositories = Collections
-                       .unmodifiableMap(new TreeMap<String, Repository>());
-
-       @SuppressWarnings("rawtypes")
-       public synchronized Repository getRepository(Map parameters)
-                       throws RepositoryException {
-               if (!parameters.containsKey(JCR_REPOSITORY_ALIAS))
-                       throw new RepositoryException("Parameter " + JCR_REPOSITORY_ALIAS
-                                       + " has to be defined.");
-               String alias = parameters.get(JCR_REPOSITORY_ALIAS).toString();
-               if (!repositories.containsKey(alias))
-                       throw new RepositoryException(
-                                       "No repository registered with alias " + alias);
-
-               return repositories.get(alias);
-       }
-
-       /** Access to the read-only map */
-       public synchronized Map<String, Repository> getRepositories() {
-               return repositories;
-       }
-
-       /** Registers a service, typically called when OSGi services are bound. */
-       @SuppressWarnings("rawtypes")
-       public synchronized void register(Repository repository, Map properties) {
-               String alias;
-               if (properties == null || !properties.containsKey(JCR_REPOSITORY_ALIAS)) {
-                       log.warn("Cannot register a repository if no "
-                                       + JCR_REPOSITORY_ALIAS + " property is specified.");
-                       return;
-               }
-               alias = properties.get(JCR_REPOSITORY_ALIAS).toString();
-               Map<String, Repository> map = new TreeMap<String, Repository>(
-                               repositories);
-               map.put(alias, repository);
-               repositories = Collections.unmodifiableMap(map);
-               setChanged();
-               notifyObservers(alias);
-       }
-
-       /** Unregisters a service, typically called when OSGi services are unbound. */
-       @SuppressWarnings("rawtypes")
-       public synchronized void unregister(Repository repository, Map properties) {
-               // TODO: also check bean name?
-               if (properties == null || !properties.containsKey(JCR_REPOSITORY_ALIAS)) {
-                       log.warn("Cannot unregister a repository without property "
-                                       + JCR_REPOSITORY_ALIAS);
-                       return;
-               }
-
-               String alias = properties.get(JCR_REPOSITORY_ALIAS).toString();
-               Map<String, Repository> map = new TreeMap<String, Repository>(
-                               repositories);
-               if (map.remove(alias) == null) {
-                       log.warn("No repository was registered with alias " + alias);
-                       return;
-               }
-               repositories = Collections.unmodifiableMap(map);
-               setChanged();
-               notifyObservers(alias);
-       }
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/JcrCallback.java b/org.argeo.server.jcr/src/org/argeo/jcr/JcrCallback.java
deleted file mode 100644 (file)
index 0c4706f..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import javax.jcr.Session;
-
-/** An arbitrary execution on a JCR session, optionally returning a result. */
-public interface JcrCallback {
-       public Object execute(Session session);
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/JcrMonitor.java b/org.argeo.server.jcr/src/org/argeo/jcr/JcrMonitor.java
deleted file mode 100644 (file)
index f04be9a..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-package org.argeo.jcr;
-
-import org.argeo.ArgeoMonitor;
-
-/**
- * Simple monitor abstraction. Inspired by Eclipse IProgressMOnitor, but without
- * dependency to it.
- */
-@SuppressWarnings("deprecation")
-public interface JcrMonitor extends ArgeoMonitor {
-       /**
-        * Constant indicating an unknown amount of work.
-        */
-       public final static int UNKNOWN = -1;
-
-       /**
-        * Notifies that the main task is beginning. This must only be called once
-        * on a given progress monitor instance.
-        * 
-        * @param name
-        *            the name (or description) of the main task
-        * @param totalWork
-        *            the total number of work units into which the main task is
-        *            been subdivided. If the value is <code>UNKNOWN</code> the
-        *            implementation is free to indicate progress in a way which
-        *            doesn't require the total number of work units in advance.
-        */
-       public void beginTask(String name, int totalWork);
-
-       /**
-        * Notifies that the work is done; that is, either the main task is
-        * completed or the user canceled it. This method may be called more than
-        * once (implementations should be prepared to handle this case).
-        */
-       public void done();
-
-       /**
-        * Returns whether cancelation of current operation has been requested.
-        * Long-running operations should poll to see if cancelation has been
-        * requested.
-        * 
-        * @return <code>true</code> if cancellation has been requested, and
-        *         <code>false</code> otherwise
-        * @see #setCanceled(boolean)
-        */
-       public boolean isCanceled();
-
-       /**
-        * Sets the cancel state to the given value.
-        * 
-        * @param value
-        *            <code>true</code> indicates that cancelation has been
-        *            requested (but not necessarily acknowledged);
-        *            <code>false</code> clears this flag
-        * @see #isCanceled()
-        */
-       public void setCanceled(boolean value);
-
-       /**
-        * Sets the task name to the given value. This method is used to restore the
-        * task label after a nested operation was executed. Normally there is no
-        * need for clients to call this method.
-        * 
-        * @param name
-        *            the name (or description) of the main task
-        * @see #beginTask(java.lang.String, int)
-        */
-       public void setTaskName(String name);
-
-       /**
-        * Notifies that a subtask of the main task is beginning. Subtasks are
-        * optional; the main task might not have subtasks.
-        * 
-        * @param name
-        *            the name (or description) of the subtask
-        */
-       public void subTask(String name);
-
-       /**
-        * Notifies that a given number of work unit of the main task has been
-        * completed. Note that this amount represents an installment, as opposed to
-        * a cumulative amount of work done to date.
-        * 
-        * @param work
-        *            a non-negative number of work units just completed
-        */
-       public void worked(int work);
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/JcrRepositoryWrapper.java b/org.argeo.server.jcr/src/org/argeo/jcr/JcrRepositoryWrapper.java
deleted file mode 100644 (file)
index 6c23aca..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import javax.jcr.Credentials;
-import javax.jcr.LoginException;
-import javax.jcr.NoSuchWorkspaceException;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.Value;
-
-/**
- * Wrapper around a JCR repository which allows to simplify configuration and
- * intercept some actions. It exposes itself as a {@link Repository}.
- */
-public abstract class JcrRepositoryWrapper implements Repository {
-       // private final static Log log = LogFactory
-       // .getLog(JcrRepositoryWrapper.class);
-
-       // wrapped repository
-       private Repository repository;
-
-       private Boolean autocreateWorkspaces = false;
-
-       /**
-        * Empty constructor, {@link #init()} should be called after properties have
-        * been set
-        */
-       public JcrRepositoryWrapper() {
-       }
-
-       /** Initializes */
-       public void init() {
-       }
-
-       /** Shutdown the repository */
-       public void destroy() throws Exception {
-       }
-
-       /*
-        * DELEGATED JCR REPOSITORY METHODS
-        */
-
-       public String getDescriptor(String key) {
-               return getRepository().getDescriptor(key);
-       }
-
-       public String[] getDescriptorKeys() {
-               return getRepository().getDescriptorKeys();
-       }
-
-       /** Central login method */
-       public Session login(Credentials credentials, String workspaceName)
-                       throws LoginException, NoSuchWorkspaceException,
-                       RepositoryException {
-               Session session;
-               try {
-                       session = getRepository().login(credentials, workspaceName);
-               } catch (NoSuchWorkspaceException e) {
-                       if (autocreateWorkspaces && workspaceName != null)
-                               session = createWorkspaceAndLogsIn(credentials, workspaceName);
-                       else
-                               throw e;
-               }
-               processNewSession(session);
-               return session;
-       }
-
-       public Session login() throws LoginException, RepositoryException {
-               return login(null, null);
-       }
-
-       public Session login(Credentials credentials) throws LoginException,
-                       RepositoryException {
-               return login(credentials, null);
-       }
-
-       public Session login(String workspaceName) throws LoginException,
-                       NoSuchWorkspaceException, RepositoryException {
-               return login(null, workspaceName);
-       }
-
-       /** Called after a session has been created, does nothing by default. */
-       protected void processNewSession(Session session) {
-       }
-
-       /** Wraps access to the repository, making sure it is available. */
-       protected synchronized Repository getRepository() {
-//             if (repository == null) {
-//                     throw new ArgeoJcrException("No repository initialized."
-//                                     + " Was the init() method called?"
-//                                     + " The destroy() method should also"
-//                                     + " be called on shutdown.");
-//             }
-               return repository;
-       }
-
-       /**
-        * Logs in to the default workspace, creates the required workspace, logs
-        * out, logs in to the required workspace.
-        */
-       protected Session createWorkspaceAndLogsIn(Credentials credentials,
-                       String workspaceName) throws RepositoryException {
-               if (workspaceName == null)
-                       throw new ArgeoJcrException("No workspace specified.");
-               Session session = getRepository().login(credentials);
-               session.getWorkspace().createWorkspace(workspaceName);
-               session.logout();
-               return getRepository().login(credentials, workspaceName);
-       }
-
-       public boolean isStandardDescriptor(String key) {
-               return getRepository().isStandardDescriptor(key);
-       }
-
-       public boolean isSingleValueDescriptor(String key) {
-               return getRepository().isSingleValueDescriptor(key);
-       }
-
-       public Value getDescriptorValue(String key) {
-               return getRepository().getDescriptorValue(key);
-       }
-
-       public Value[] getDescriptorValues(String key) {
-               return getRepository().getDescriptorValues(key);
-       }
-
-       public synchronized void setRepository(Repository repository) {
-               this.repository = repository;
-       }
-
-       public void setAutocreateWorkspaces(Boolean autocreateWorkspaces) {
-               this.autocreateWorkspaces = autocreateWorkspaces;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/JcrResourceAdapter.java b/org.argeo.server.jcr/src/org/argeo/jcr/JcrResourceAdapter.java
deleted file mode 100644 (file)
index 1ccce4f..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.List;
-
-import javax.jcr.Binary;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.nodetype.NodeType;
-import javax.jcr.version.Version;
-import javax.jcr.version.VersionHistory;
-import javax.jcr.version.VersionIterator;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * Bridge Spring resources and JCR folder / files semantics (nt:folder /
- * nt:file), supporting versioning as well.
- */
-public class JcrResourceAdapter {
-       private final static Log log = LogFactory.getLog(JcrResourceAdapter.class);
-
-       private Session session;
-
-       private Boolean versioning = true;
-       private String defaultEncoding = "UTF-8";
-
-       // private String restoreBase = "/.restore";
-
-       public JcrResourceAdapter() {
-       }
-
-       public JcrResourceAdapter(Session session) {
-               this.session = session;
-       }
-
-       public void mkdirs(String path) {
-               JcrUtils.mkdirs(session(), path, NodeType.NT_FOLDER,
-                               NodeType.NT_FOLDER, versioning);
-       }
-
-       public void create(String path, InputStream in, String mimeType) {
-               try {
-                       if (session().itemExists(path)) {
-                               throw new ArgeoJcrException("Node " + path + " already exists.");
-                       }
-
-                       int index = path.lastIndexOf('/');
-                       String parentPath = path.substring(0, index);
-                       if (parentPath.equals(""))
-                               parentPath = "/";
-                       String fileName = path.substring(index + 1);
-                       if (!session().itemExists(parentPath))
-                               throw new ArgeoJcrException("Parent folder of node " + path
-                                               + " does not exist: " + parentPath);
-
-                       Node folderNode = (Node) session().getItem(parentPath);
-                       Node fileNode = folderNode.addNode(fileName, "nt:file");
-
-                       Node contentNode = fileNode.addNode(Property.JCR_CONTENT,
-                                       "nt:resource");
-                       if (mimeType != null)
-                               contentNode.setProperty(Property.JCR_MIMETYPE, mimeType);
-                       contentNode.setProperty(Property.JCR_ENCODING, defaultEncoding);
-                       Binary binary = session().getValueFactory().createBinary(in);
-                       contentNode.setProperty(Property.JCR_DATA, binary);
-                       JcrUtils.closeQuietly(binary);
-                       Calendar lastModified = Calendar.getInstance();
-                       // lastModified.setTimeInMillis(file.lastModified());
-                       contentNode.setProperty(Property.JCR_LAST_MODIFIED, lastModified);
-                       // resNode.addMixin("mix:referenceable");
-
-                       if (versioning)
-                               fileNode.addMixin("mix:versionable");
-
-                       session().save();
-
-                       if (versioning)
-                               session().getWorkspace().getVersionManager()
-                                               .checkin(fileNode.getPath());
-
-                       if (log.isDebugEnabled())
-                               log.debug("Created " + path);
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot create node for " + path, e);
-               }
-
-       }
-
-       public void update(String path, InputStream in) {
-               try {
-
-                       if (!session().itemExists(path)) {
-                               String type = null;
-                               // FIXME: using javax.activation leads to conflict between Java
-                               // 1.5 and 1.6 (since javax.activation was included in Java 1.6)
-                               // String type = new MimetypesFileTypeMap()
-                               // .getContentType(FilenameUtils.getName(path));
-                               create(path, in, type);
-                               return;
-                       }
-
-                       Node fileNode = (Node) session().getItem(path);
-                       Node contentNode = fileNode.getNode(Property.JCR_CONTENT);
-                       if (versioning)
-                               session().getWorkspace().getVersionManager()
-                                               .checkout(fileNode.getPath());
-                       Binary binary = session().getValueFactory().createBinary(in);
-                       contentNode.setProperty(Property.JCR_DATA, binary);
-                       JcrUtils.closeQuietly(binary);
-                       Calendar lastModified = Calendar.getInstance();
-                       // lastModified.setTimeInMillis(file.lastModified());
-                       contentNode.setProperty(Property.JCR_LAST_MODIFIED, lastModified);
-
-                       session().save();
-                       if (versioning)
-                               session().getWorkspace().getVersionManager()
-                                               .checkin(fileNode.getPath());
-
-                       if (log.isDebugEnabled())
-                               log.debug("Updated " + path);
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot update node " + path, e);
-               }
-       }
-
-       public List<Calendar> listVersions(String path) {
-               if (!versioning)
-                       throw new ArgeoJcrException("Versioning is not activated");
-
-               try {
-                       List<Calendar> versions = new ArrayList<Calendar>();
-                       Node fileNode = (Node) session().getItem(path);
-                       VersionHistory history = session().getWorkspace()
-                                       .getVersionManager().getVersionHistory(fileNode.getPath());
-                       for (VersionIterator it = history.getAllVersions(); it.hasNext();) {
-                               Version version = (Version) it.next();
-                               versions.add(version.getCreated());
-                               if (log.isTraceEnabled()) {
-                                       log.debug(version);
-                                       // debug(version);
-                               }
-                       }
-                       return versions;
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot list version of node " + path, e);
-               }
-       }
-
-       public InputStream retrieve(String path) {
-               try {
-                       Node node = (Node) session().getItem(
-                                       path + "/" + Property.JCR_CONTENT);
-                       Property property = node.getProperty(Property.JCR_DATA);
-                       return property.getBinary().getStream();
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot retrieve " + path, e);
-               }
-       }
-
-       public synchronized InputStream retrieve(String path, Integer revision) {
-               if (!versioning)
-                       throw new ArgeoJcrException("Versioning is not activated");
-
-               try {
-                       Node fileNode = (Node) session().getItem(path);
-                       VersionHistory history = session().getWorkspace()
-                                       .getVersionManager().getVersionHistory(fileNode.getPath());
-                       int count = 0;
-                       Version version = null;
-                       for (VersionIterator it = history.getAllVersions(); it.hasNext();) {
-                               version = (Version) it.next();
-                               if (count == revision + 1) {
-                                       InputStream in = fromVersion(version);
-                                       if (log.isDebugEnabled())
-                                               log.debug("Retrieved " + path + " at revision "
-                                                               + revision);
-                                       return in;
-                               }
-                               count++;
-                       }
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot retrieve version " + revision
-                                       + " of " + path, e);
-               }
-
-               throw new ArgeoJcrException("Version " + revision
-                               + " does not exist for node " + path);
-       }
-
-       protected InputStream fromVersion(Version version)
-                       throws RepositoryException {
-               Node frozenNode = version.getNode("jcr:frozenNode");
-               InputStream in = frozenNode.getNode(Property.JCR_CONTENT)
-                               .getProperty(Property.JCR_DATA).getBinary().getStream();
-               return in;
-       }
-
-       protected Session session() {
-               return session;
-       }
-
-       public void setVersioning(Boolean versioning) {
-               this.versioning = versioning;
-       }
-
-       public void setDefaultEncoding(String defaultEncoding) {
-               this.defaultEncoding = defaultEncoding;
-       }
-
-       protected String fill(Integer number) {
-               int size = 4;
-               String str = number.toString();
-               for (int i = str.length(); i < size; i++) {
-                       str = "0" + str;
-               }
-               return str;
-       }
-
-       public void setSession(Session session) {
-               this.session = session;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/JcrUrlStreamHandler.java b/org.argeo.server.jcr/src/org/argeo/jcr/JcrUrlStreamHandler.java
deleted file mode 100644 (file)
index a777639..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
-
-import javax.jcr.Item;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.nodetype.NodeType;
-
-/** URL stream handler able to deal with nt:file node and properties. NOT FINISHED */
-public class JcrUrlStreamHandler extends URLStreamHandler {
-       private final Session session;
-
-       public JcrUrlStreamHandler(Session session) {
-               this.session = session;
-       }
-
-       @Override
-       protected URLConnection openConnection(final URL u) throws IOException {
-               // TODO Auto-generated method stub
-               return new URLConnection(u) {
-
-                       @Override
-                       public void connect() throws IOException {
-                               String itemPath = u.getPath();
-                               try {
-                                       if (!session.itemExists(itemPath))
-                                               throw new IOException("No item under " + itemPath);
-
-                                       Item item = session.getItem(u.getPath());
-                                       if (item.isNode()) {
-                                               // this should be a nt:file node
-                                               Node node = (Node) item;
-                                               if (!node.getPrimaryNodeType().isNodeType(
-                                                               NodeType.NT_FILE))
-                                                       throw new IOException("Node " + node + " is not a "
-                                                                       + NodeType.NT_FILE);
-
-                                       } else {
-                                               Property property = (Property) item;
-                                               if(property.getType()==PropertyType.BINARY){
-                                                       //Binary binary = property.getBinary();
-                                                       
-                                               }
-                                       }
-                               } catch (RepositoryException e) {
-                                       IOException ioe = new IOException(
-                                                       "Unexpected JCR exception");
-                                       ioe.initCause(e);
-                                       throw ioe;
-                               }
-                       }
-
-                       @Override
-                       public InputStream getInputStream() throws IOException {
-                               // TODO Auto-generated method stub
-                               return super.getInputStream();
-                       }
-
-               };
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/JcrUtils.java b/org.argeo.server.jcr/src/org/argeo/jcr/JcrUtils.java
deleted file mode 100644 (file)
index 98a2da8..0000000
+++ /dev/null
@@ -1,1592 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.security.Principal;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import javax.jcr.Binary;
-import javax.jcr.NamespaceRegistry;
-import javax.jcr.NoSuchWorkspaceException;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
-import javax.jcr.PropertyType;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.Value;
-import javax.jcr.Workspace;
-import javax.jcr.nodetype.NodeType;
-import javax.jcr.observation.EventListener;
-import javax.jcr.query.Query;
-import javax.jcr.query.QueryResult;
-import javax.jcr.security.AccessControlEntry;
-import javax.jcr.security.AccessControlList;
-import javax.jcr.security.AccessControlManager;
-import javax.jcr.security.AccessControlPolicy;
-import javax.jcr.security.AccessControlPolicyIterator;
-import javax.jcr.security.Privilege;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoMonitor;
-import org.argeo.util.security.DigestUtils;
-import org.argeo.util.security.SimplePrincipal;
-
-/** Utility methods to simplify common JCR operations. */
-public class JcrUtils implements ArgeoJcrConstants {
-
-       final private static Log log = LogFactory.getLog(JcrUtils.class);
-
-       /**
-        * Not complete yet. See
-        * http://www.day.com/specs/jcr/2.0/3_Repository_Model.html#3.2.2%20Local
-        * %20Names
-        */
-       public final static char[] INVALID_NAME_CHARACTERS = { '/', ':', '[', ']',
-                       '|', '*', /*
-                                        * invalid XML chars :
-                                        */
-                       '<', '>', '&' };
-
-       /** Prevents instantiation */
-       private JcrUtils() {
-       }
-
-       /**
-        * Queries one single node.
-        * 
-        * @return one single node or null if none was found
-        * @throws ArgeoJcrException
-        *             if more than one node was found
-        */
-       public static Node querySingleNode(Query query) {
-               NodeIterator nodeIterator;
-               try {
-                       QueryResult queryResult = query.execute();
-                       nodeIterator = queryResult.getNodes();
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot execute query " + query, e);
-               }
-               Node node;
-               if (nodeIterator.hasNext())
-                       node = nodeIterator.nextNode();
-               else
-                       return null;
-
-               if (nodeIterator.hasNext())
-                       throw new ArgeoJcrException("Query returned more than one node.");
-               return node;
-       }
-
-       /** Retrieves the node name from the provided path */
-       public static String nodeNameFromPath(String path) {
-               if (path.equals("/"))
-                       return "";
-               if (path.charAt(0) != '/')
-                       throw new ArgeoJcrException("Path " + path + " must start with a '/'");
-               String pathT = path;
-               if (pathT.charAt(pathT.length() - 1) == '/')
-                       pathT = pathT.substring(0, pathT.length() - 2);
-
-               int index = pathT.lastIndexOf('/');
-               return pathT.substring(index + 1);
-       }
-
-       /** Retrieves the parent path of the provided path */
-       public static String parentPath(String path) {
-               if (path.equals("/"))
-                       throw new ArgeoJcrException("Root path '/' has no parent path");
-               if (path.charAt(0) != '/')
-                       throw new ArgeoJcrException("Path " + path + " must start with a '/'");
-               String pathT = path;
-               if (pathT.charAt(pathT.length() - 1) == '/')
-                       pathT = pathT.substring(0, pathT.length() - 2);
-
-               int index = pathT.lastIndexOf('/');
-               return pathT.substring(0, index);
-       }
-
-       /** The provided data as a path ('/' at the end, not the beginning) */
-       public static String dateAsPath(Calendar cal) {
-               return dateAsPath(cal, false);
-       }
-
-       /**
-        * Creates a deep path based on a URL:
-        * http://subdomain.example.com/to/content?args =>
-        * com/example/subdomain/to/content
-        */
-       public static String urlAsPath(String url) {
-               try {
-                       URL u = new URL(url);
-                       StringBuffer path = new StringBuffer(url.length());
-                       // invert host
-                       path.append(hostAsPath(u.getHost()));
-                       // we don't put port since it may not always be there and may change
-                       path.append(u.getPath());
-                       return path.toString();
-               } catch (MalformedURLException e) {
-                       throw new ArgeoJcrException("Cannot generate URL path for " + url, e);
-               }
-       }
-
-       /** Set the {@link NodeType#NT_ADDRESS} properties based on this URL. */
-       public static void urlToAddressProperties(Node node, String url) {
-               try {
-                       URL u = new URL(url);
-                       node.setProperty(Property.JCR_PROTOCOL, u.getProtocol());
-                       node.setProperty(Property.JCR_HOST, u.getHost());
-                       node.setProperty(Property.JCR_PORT, Integer.toString(u.getPort()));
-                       node.setProperty(Property.JCR_PATH, normalizePath(u.getPath()));
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot set URL " + url
-                                       + " as nt:address properties", e);
-               }
-       }
-
-       /** Build URL based on the {@link NodeType#NT_ADDRESS} properties. */
-       public static String urlFromAddressProperties(Node node) {
-               try {
-                       URL u = new URL(
-                                       node.getProperty(Property.JCR_PROTOCOL).getString(), node
-                                                       .getProperty(Property.JCR_HOST).getString(),
-                                       (int) node.getProperty(Property.JCR_PORT).getLong(), node
-                                                       .getProperty(Property.JCR_PATH).getString());
-                       return u.toString();
-               } catch (Exception e) {
-                       throw new ArgeoJcrException(
-                                       "Cannot get URL from nt:address properties of " + node, e);
-               }
-       }
-
-       /*
-        * PATH UTILITIES
-        */
-
-       /** Make sure that: starts with '/', do not end with '/', do not have '//' */
-       public static String normalizePath(String path) {
-               List<String> tokens = tokenize(path);
-               StringBuffer buf = new StringBuffer(path.length());
-               for (String token : tokens) {
-                       buf.append('/');
-                       buf.append(token);
-               }
-               return buf.toString();
-       }
-
-       /**
-        * Creates a path from a FQDN, inverting the order of the component:
-        * www.argeo.org => org.argeo.www
-        */
-       public static String hostAsPath(String host) {
-               StringBuffer path = new StringBuffer(host.length());
-               String[] hostTokens = host.split("\\.");
-               for (int i = hostTokens.length - 1; i >= 0; i--) {
-                       path.append(hostTokens[i]);
-                       if (i != 0)
-                               path.append('/');
-               }
-               return path.toString();
-       }
-
-       /**
-        * Creates a path from a UUID (e.g. 6ebda899-217d-4bf1-abe4-2839085c8f3c =>
-        * 6ebda899-217d/4bf1/abe4/2839085c8f3c/). '/' at the end, not the beginning
-        */
-       public static String uuidAsPath(String uuid) {
-               StringBuffer path = new StringBuffer(uuid.length());
-               String[] tokens = uuid.split("-");
-               for (int i = 0; i < tokens.length; i++) {
-                       path.append(tokens[i]);
-                       if (i != 0)
-                               path.append('/');
-               }
-               return path.toString();
-       }
-
-       /**
-        * The provided data as a path ('/' at the end, not the beginning)
-        * 
-        * @param cal
-        *            the date
-        * @param addHour
-        *            whether to add hour as well
-        */
-       public static String dateAsPath(Calendar cal, Boolean addHour) {
-               StringBuffer buf = new StringBuffer(14);
-               buf.append('Y');
-               buf.append(cal.get(Calendar.YEAR));
-               buf.append('/');
-
-               int month = cal.get(Calendar.MONTH) + 1;
-               buf.append('M');
-               if (month < 10)
-                       buf.append(0);
-               buf.append(month);
-               buf.append('/');
-
-               int day = cal.get(Calendar.DAY_OF_MONTH);
-               buf.append('D');
-               if (day < 10)
-                       buf.append(0);
-               buf.append(day);
-               buf.append('/');
-
-               if (addHour) {
-                       int hour = cal.get(Calendar.HOUR_OF_DAY);
-                       buf.append('H');
-                       if (hour < 10)
-                               buf.append(0);
-                       buf.append(hour);
-                       buf.append('/');
-               }
-               return buf.toString();
-
-       }
-
-       /** Converts in one call a string into a gregorian calendar. */
-       public static Calendar parseCalendar(DateFormat dateFormat, String value) {
-               try {
-                       Date date = dateFormat.parse(value);
-                       Calendar calendar = new GregorianCalendar();
-                       calendar.setTime(date);
-                       return calendar;
-               } catch (ParseException e) {
-                       throw new ArgeoJcrException("Cannot parse " + value
-                                       + " with date format " + dateFormat, e);
-               }
-
-       }
-
-       /** The last element of a path. */
-       public static String lastPathElement(String path) {
-               if (path.charAt(path.length() - 1) == '/')
-                       throw new ArgeoJcrException("Path " + path + " cannot end with '/'");
-               int index = path.lastIndexOf('/');
-               if (index < 0)
-                       return path;
-               return path.substring(index + 1);
-       }
-
-       /**
-        * Call {@link Node#getName()} without exceptions (useful in super
-        * constructors).
-        */
-       public static String getNameQuietly(Node node) {
-               try {
-                       return node.getName();
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot get name from " + node, e);
-               }
-       }
-
-       /**
-        * Call {@link Node#getProperty(String)} without exceptions (useful in super
-        * constructors).
-        */
-       public static String getStringPropertyQuietly(Node node, String propertyName) {
-               try {
-                       return node.getProperty(propertyName).getString();
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot get name from " + node, e);
-               }
-       }
-
-       /**
-        * Routine that get the child with this name, adding id it does not already
-        * exist
-        */
-       public static Node getOrAdd(Node parent, String childName,
-                       String childPrimaryNodeType) throws RepositoryException {
-               return parent.hasNode(childName) ? parent.getNode(childName) : parent
-                               .addNode(childName, childPrimaryNodeType);
-       }
-
-       /**
-        * Routine that get the child with this name, adding id it does not already
-        * exist
-        */
-       public static Node getOrAdd(Node parent, String childName)
-                       throws RepositoryException {
-               return parent.hasNode(childName) ? parent.getNode(childName) : parent
-                               .addNode(childName);
-       }
-
-       /** Convert a {@link NodeIterator} to a list of {@link Node} */
-       public static List<Node> nodeIteratorToList(NodeIterator nodeIterator) {
-               List<Node> nodes = new ArrayList<Node>();
-               while (nodeIterator.hasNext()) {
-                       nodes.add(nodeIterator.nextNode());
-               }
-               return nodes;
-       }
-
-       /*
-        * PROPERTIES
-        */
-
-       /**
-        * Concisely get the string value of a property or null if this node doesn't
-        * have this property
-        */
-       public static String get(Node node, String propertyName) {
-               try {
-                       if (!node.hasProperty(propertyName))
-                               return null;
-                       return node.getProperty(propertyName).getString();
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot get property " + propertyName
-                                       + " of " + node, e);
-               }
-       }
-
-       /** Concisely get the boolean value of a property */
-       public static Boolean check(Node node, String propertyName) {
-               try {
-                       return node.getProperty(propertyName).getBoolean();
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot get property " + propertyName
-                                       + " of " + node, e);
-               }
-       }
-
-       /** Concisely get the bytes array value of a property */
-       public static byte[] getBytes(Node node, String propertyName) {
-               try {
-                       return getBinaryAsBytes(node.getProperty(propertyName));
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot get property " + propertyName
-                                       + " of " + node, e);
-               }
-       }
-
-       /** Creates the nodes making path, if they don't exist. */
-       public static Node mkdirs(Session session, String path) {
-               return mkdirs(session, path, null, null, false);
-       }
-
-       /**
-        * use {@link #mkdirs(Session, String, String, String, Boolean)} instead.
-        * 
-        * @deprecated
-        */
-       @Deprecated
-       public static Node mkdirs(Session session, String path, String type,
-                       Boolean versioning) {
-               return mkdirs(session, path, type, type, false);
-       }
-
-       /**
-        * @param type
-        *            the type of the leaf node
-        */
-       public static Node mkdirs(Session session, String path, String type) {
-               return mkdirs(session, path, type, null, false);
-       }
-
-       /**
-        * Create sub nodes relative to a parent node
-        * 
-        * @param nodeType
-        *            the type of the leaf node
-        */
-       public static Node mkdirs(Node parentNode, String relativePath,
-                       String nodeType) {
-               return mkdirs(parentNode, relativePath, nodeType, null);
-       }
-
-       /**
-        * Create sub nodes relative to a parent node
-        * 
-        * @param nodeType
-        *            the type of the leaf node
-        */
-       public static Node mkdirs(Node parentNode, String relativePath,
-                       String nodeType, String intermediaryNodeType) {
-               List<String> tokens = tokenize(relativePath);
-               Node currParent = parentNode;
-               try {
-                       for (int i = 0; i < tokens.size(); i++) {
-                               String name = tokens.get(i);
-                               if (currParent.hasNode(name)) {
-                                       currParent = currParent.getNode(name);
-                               } else {
-                                       if (i != (tokens.size() - 1)) {// intermediary
-                                               currParent = currParent.addNode(name,
-                                                               intermediaryNodeType);
-                                       } else {// leaf
-                                               currParent = currParent.addNode(name, nodeType);
-                                       }
-                               }
-                       }
-                       return currParent;
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot mkdirs relative path "
-                                       + relativePath + " from " + parentNode, e);
-               }
-       }
-
-       /**
-        * Synchronized and save is performed, to avoid race conditions in
-        * initializers leading to duplicate nodes.
-        */
-       public synchronized static Node mkdirsSafe(Session session, String path,
-                       String type) {
-               try {
-                       if (session.hasPendingChanges())
-                               throw new ArgeoJcrException(
-                                               "Session has pending changes, save them first.");
-                       Node node = mkdirs(session, path, type);
-                       session.save();
-                       return node;
-               } catch (RepositoryException e) {
-                       discardQuietly(session);
-                       throw new ArgeoJcrException("Cannot safely make directories", e);
-               }
-       }
-
-       public synchronized static Node mkdirsSafe(Session session, String path) {
-               return mkdirsSafe(session, path, null);
-       }
-
-       /**
-        * Creates the nodes making path, if they don't exist. This is up to the
-        * caller to save the session. Use with caution since it can create
-        * duplicate nodes if used concurrently.
-        */
-       public static Node mkdirs(Session session, String path, String type,
-                       String intermediaryNodeType, Boolean versioning) {
-               try {
-                       if (path.equals('/'))
-                               return session.getRootNode();
-
-                       if (session.itemExists(path)) {
-                               Node node = session.getNode(path);
-                               // check type
-                               if (type != null && !node.isNodeType(type)
-                                               && !node.getPath().equals("/"))
-                                       throw new ArgeoJcrException("Node " + node
-                                                       + " exists but is of type "
-                                                       + node.getPrimaryNodeType().getName()
-                                                       + " not of type " + type);
-                               // TODO: check versioning
-                               return node;
-                       }
-
-                       StringBuffer current = new StringBuffer("/");
-                       Node currentNode = session.getRootNode();
-                       Iterator<String> it = tokenize(path).iterator();
-                       while (it.hasNext()) {
-                               String part = it.next();
-                               current.append(part).append('/');
-                               if (!session.itemExists(current.toString())) {
-                                       if (!it.hasNext() && type != null)
-                                               currentNode = currentNode.addNode(part, type);
-                                       else if (it.hasNext() && intermediaryNodeType != null)
-                                               currentNode = currentNode.addNode(part,
-                                                               intermediaryNodeType);
-                                       else
-                                               currentNode = currentNode.addNode(part);
-                                       if (versioning)
-                                               currentNode.addMixin(NodeType.MIX_VERSIONABLE);
-                                       if (log.isTraceEnabled())
-                                               log.debug("Added folder " + part + " as " + current);
-                               } else {
-                                       currentNode = (Node) session.getItem(current.toString());
-                               }
-                       }
-                       return currentNode;
-               } catch (RepositoryException e) {
-                       discardQuietly(session);
-                       throw new ArgeoJcrException("Cannot mkdirs " + path, e);
-               } finally {
-               }
-       }
-
-       /** Convert a path to the list of its tokens */
-       public static List<String> tokenize(String path) {
-               List<String> tokens = new ArrayList<String>();
-               boolean optimized = false;
-               if (!optimized) {
-                       String[] rawTokens = path.split("/");
-                       for (String token : rawTokens) {
-                               if (!token.equals(""))
-                                       tokens.add(token);
-                       }
-               } else {
-                       StringBuffer curr = new StringBuffer();
-                       char[] arr = path.toCharArray();
-                       chars: for (int i = 0; i < arr.length; i++) {
-                               char c = arr[i];
-                               if (c == '/') {
-                                       if (i == 0 || (i == arr.length - 1))
-                                               continue chars;
-                                       if (curr.length() > 0) {
-                                               tokens.add(curr.toString());
-                                               curr = new StringBuffer();
-                                       }
-                               } else
-                                       curr.append(c);
-                       }
-                       if (curr.length() > 0) {
-                               tokens.add(curr.toString());
-                               curr = new StringBuffer();
-                       }
-               }
-               return Collections.unmodifiableList(tokens);
-       }
-
-       /**
-        * Safe and repository implementation independent registration of a
-        * namespace.
-        */
-       public static void registerNamespaceSafely(Session session, String prefix,
-                       String uri) {
-               try {
-                       registerNamespaceSafely(session.getWorkspace()
-                                       .getNamespaceRegistry(), prefix, uri);
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot find namespace registry", e);
-               }
-       }
-
-       /**
-        * Safe and repository implementation independent registration of a
-        * namespace.
-        */
-       public static void registerNamespaceSafely(NamespaceRegistry nr,
-                       String prefix, String uri) {
-               try {
-                       String[] prefixes = nr.getPrefixes();
-                       for (String pref : prefixes)
-                               if (pref.equals(prefix)) {
-                                       String registeredUri = nr.getURI(pref);
-                                       if (!registeredUri.equals(uri))
-                                               throw new ArgeoJcrException("Prefix " + pref
-                                                               + " already registered for URI "
-                                                               + registeredUri
-                                                               + " which is different from provided URI "
-                                                               + uri);
-                                       else
-                                               return;// skip
-                               }
-                       nr.registerNamespace(prefix, uri);
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot register namespace " + uri
-                                       + " under prefix " + prefix, e);
-               }
-       }
-
-       /** Recursively outputs the contents of the given node. */
-       public static void debug(Node node) {
-               debug(node, log);
-       }
-
-       /** Recursively outputs the contents of the given node. */
-       public static void debug(Node node, Log log) {
-               try {
-                       // First output the node path
-                       log.debug(node.getPath());
-                       // Skip the virtual (and large!) jcr:system subtree
-                       if (node.getName().equals("jcr:system")) {
-                               return;
-                       }
-
-                       // Then the children nodes (recursive)
-                       NodeIterator it = node.getNodes();
-                       while (it.hasNext()) {
-                               Node childNode = it.nextNode();
-                               debug(childNode, log);
-                       }
-
-                       // Then output the properties
-                       PropertyIterator properties = node.getProperties();
-                       // log.debug("Property are : ");
-
-                       properties: while (properties.hasNext()) {
-                               Property property = properties.nextProperty();
-                               if (property.getType() == PropertyType.BINARY)
-                                       continue properties;// skip
-                               if (property.getDefinition().isMultiple()) {
-                                       // A multi-valued property, print all values
-                                       Value[] values = property.getValues();
-                                       for (int i = 0; i < values.length; i++) {
-                                               log.debug(property.getPath() + "="
-                                                               + values[i].getString());
-                                       }
-                               } else {
-                                       // A single-valued property
-                                       log.debug(property.getPath() + "=" + property.getString());
-                               }
-                       }
-               } catch (Exception e) {
-                       log.error("Could not debug " + node, e);
-               }
-
-       }
-
-       /** Logs the effective access control policies */
-       public static void logEffectiveAccessPolicies(Node node) {
-               try {
-                       logEffectiveAccessPolicies(node.getSession(), node.getPath());
-               } catch (RepositoryException e) {
-                       log.error("Cannot log effective access policies of " + node, e);
-               }
-       }
-
-       /** Logs the effective access control policies */
-       public static void logEffectiveAccessPolicies(Session session, String path) {
-               if (!log.isDebugEnabled())
-                       return;
-
-               try {
-                       AccessControlPolicy[] effectivePolicies = session
-                                       .getAccessControlManager().getEffectivePolicies(path);
-                       if (effectivePolicies.length > 0) {
-                               for (AccessControlPolicy policy : effectivePolicies) {
-                                       if (policy instanceof AccessControlList) {
-                                               AccessControlList acl = (AccessControlList) policy;
-                                               log.debug("Access control list for " + path + "\n"
-                                                               + accessControlListSummary(acl));
-                                       }
-                               }
-                       } else {
-                               log.debug("No effective access control policy for " + path);
-                       }
-               } catch (RepositoryException e) {
-                       log.error("Cannot log effective access policies of " + path, e);
-               }
-       }
-
-       /** Returns a human-readable summary of this access control list. */
-       public static String accessControlListSummary(AccessControlList acl) {
-               StringBuffer buf = new StringBuffer("");
-               try {
-                       for (AccessControlEntry ace : acl.getAccessControlEntries()) {
-                               buf.append('\t').append(ace.getPrincipal().getName())
-                                               .append('\n');
-                               for (Privilege priv : ace.getPrivileges())
-                                       buf.append("\t\t").append(priv.getName()).append('\n');
-                       }
-                       return buf.toString();
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot write summary of " + acl, e);
-               }
-       }
-
-       /**
-        * Copies recursively the content of a node to another one. Do NOT copy the
-        * property values of {@link NodeType#MIX_CREATED} and
-        * {@link NodeType#MIX_LAST_MODIFIED}, but update the
-        * {@link Property#JCR_LAST_MODIFIED} and
-        * {@link Property#JCR_LAST_MODIFIED_BY} properties if the target node has
-        * the {@link NodeType#MIX_LAST_MODIFIED} mixin.
-        */
-       public static void copy(Node fromNode, Node toNode) {
-               try {
-                       if (toNode.getDefinition().isProtected())
-                               return;
-
-                       // process properties
-                       PropertyIterator pit = fromNode.getProperties();
-                       properties: while (pit.hasNext()) {
-                               Property fromProperty = pit.nextProperty();
-                               String propertyName = fromProperty.getName();
-                               if (toNode.hasProperty(propertyName)
-                                               && toNode.getProperty(propertyName).getDefinition()
-                                                               .isProtected())
-                                       continue properties;
-
-                               if (fromProperty.getDefinition().isProtected())
-                                       continue properties;
-
-                               if (propertyName.equals("jcr:created")
-                                               || propertyName.equals("jcr:createdBy")
-                                               || propertyName.equals("jcr:lastModified")
-                                               || propertyName.equals("jcr:lastModifiedBy"))
-                                       continue properties;
-
-                               if (fromProperty.isMultiple()) {
-                                       toNode.setProperty(propertyName, fromProperty.getValues());
-                               } else {
-                                       toNode.setProperty(propertyName, fromProperty.getValue());
-                               }
-                       }
-
-                       // update jcr:lastModified and jcr:lastModifiedBy in toNode in case
-                       // they existed, before adding the mixins
-                       updateLastModified(toNode);
-
-                       // add mixins
-                       for (NodeType mixinType : fromNode.getMixinNodeTypes()) {
-                               toNode.addMixin(mixinType.getName());
-                       }
-
-                       // process children nodes
-                       NodeIterator nit = fromNode.getNodes();
-                       while (nit.hasNext()) {
-                               Node fromChild = nit.nextNode();
-                               Integer index = fromChild.getIndex();
-                               String nodeRelPath = fromChild.getName() + "[" + index + "]";
-                               Node toChild;
-                               if (toNode.hasNode(nodeRelPath))
-                                       toChild = toNode.getNode(nodeRelPath);
-                               else
-                                       toChild = toNode.addNode(fromChild.getName(), fromChild
-                                                       .getPrimaryNodeType().getName());
-                               copy(fromChild, toChild);
-                       }
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot copy " + fromNode + " to "
-                                       + toNode, e);
-               }
-       }
-
-       /**
-        * Check whether all first-level properties (except jcr:* properties) are
-        * equal. Skip jcr:* properties
-        */
-       public static Boolean allPropertiesEquals(Node reference, Node observed,
-                       Boolean onlyCommonProperties) {
-               try {
-                       PropertyIterator pit = reference.getProperties();
-                       props: while (pit.hasNext()) {
-                               Property propReference = pit.nextProperty();
-                               String propName = propReference.getName();
-                               if (propName.startsWith("jcr:"))
-                                       continue props;
-
-                               if (!observed.hasProperty(propName))
-                                       if (onlyCommonProperties)
-                                               continue props;
-                                       else
-                                               return false;
-                               // TODO: deal with multiple property values?
-                               if (!observed.getProperty(propName).getValue()
-                                               .equals(propReference.getValue()))
-                                       return false;
-                       }
-                       return true;
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot check all properties equals of "
-                                       + reference + " and " + observed, e);
-               }
-       }
-
-       public static Map<String, PropertyDiff> diffProperties(Node reference,
-                       Node observed) {
-               Map<String, PropertyDiff> diffs = new TreeMap<String, PropertyDiff>();
-               diffPropertiesLevel(diffs, null, reference, observed);
-               return diffs;
-       }
-
-       /**
-        * Compare the properties of two nodes. Recursivity to child nodes is not
-        * yet supported. Skip jcr:* properties.
-        */
-       static void diffPropertiesLevel(Map<String, PropertyDiff> diffs,
-                       String baseRelPath, Node reference, Node observed) {
-               try {
-                       // check removed and modified
-                       PropertyIterator pit = reference.getProperties();
-                       props: while (pit.hasNext()) {
-                               Property p = pit.nextProperty();
-                               String name = p.getName();
-                               if (name.startsWith("jcr:"))
-                                       continue props;
-
-                               if (!observed.hasProperty(name)) {
-                                       String relPath = propertyRelPath(baseRelPath, name);
-                                       PropertyDiff pDiff = new PropertyDiff(PropertyDiff.REMOVED,
-                                                       relPath, p.getValue(), null);
-                                       diffs.put(relPath, pDiff);
-                               } else {
-                                       if (p.isMultiple()) {
-                                               // FIXME implement multiple
-                                       } else {
-                                               Value referenceValue = p.getValue();
-                                               Value newValue = observed.getProperty(name).getValue();
-                                               if (!referenceValue.equals(newValue)) {
-                                                       String relPath = propertyRelPath(baseRelPath, name);
-                                                       PropertyDiff pDiff = new PropertyDiff(
-                                                                       PropertyDiff.MODIFIED, relPath,
-                                                                       referenceValue, newValue);
-                                                       diffs.put(relPath, pDiff);
-                                               }
-                                       }
-                               }
-                       }
-                       // check added
-                       pit = observed.getProperties();
-                       props: while (pit.hasNext()) {
-                               Property p = pit.nextProperty();
-                               String name = p.getName();
-                               if (name.startsWith("jcr:"))
-                                       continue props;
-                               if (!reference.hasProperty(name)) {
-                                       if (p.isMultiple()) {
-                                               // FIXME implement multiple
-                                       } else {
-                                               String relPath = propertyRelPath(baseRelPath, name);
-                                               PropertyDiff pDiff = new PropertyDiff(
-                                                               PropertyDiff.ADDED, relPath, null, p.getValue());
-                                               diffs.put(relPath, pDiff);
-                                       }
-                               }
-                       }
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot diff " + reference + " and "
-                                       + observed, e);
-               }
-       }
-
-       /**
-        * Compare only a restricted list of properties of two nodes. No
-        * recursivity.
-        * 
-        */
-       public static Map<String, PropertyDiff> diffProperties(Node reference,
-                       Node observed, List<String> properties) {
-               Map<String, PropertyDiff> diffs = new TreeMap<String, PropertyDiff>();
-               try {
-                       Iterator<String> pit = properties.iterator();
-
-                       props: while (pit.hasNext()) {
-                               String name = pit.next();
-                               if (!reference.hasProperty(name)) {
-                                       if (!observed.hasProperty(name))
-                                               continue props;
-                                       Value val = observed.getProperty(name).getValue();
-                                       try {
-                                               // empty String but not null
-                                               if ("".equals(val.getString()))
-                                                       continue props;
-                                       } catch (Exception e) {
-                                               // not parseable as String, silent
-                                       }
-                                       PropertyDiff pDiff = new PropertyDiff(PropertyDiff.ADDED,
-                                                       name, null, val);
-                                       diffs.put(name, pDiff);
-                               } else if (!observed.hasProperty(name)) {
-                                       PropertyDiff pDiff = new PropertyDiff(PropertyDiff.REMOVED,
-                                                       name, reference.getProperty(name).getValue(), null);
-                                       diffs.put(name, pDiff);
-                               } else {
-                                       Value referenceValue = reference.getProperty(name)
-                                                       .getValue();
-                                       Value newValue = observed.getProperty(name).getValue();
-                                       if (!referenceValue.equals(newValue)) {
-                                               PropertyDiff pDiff = new PropertyDiff(
-                                                               PropertyDiff.MODIFIED, name, referenceValue,
-                                                               newValue);
-                                               diffs.put(name, pDiff);
-                                       }
-                               }
-                       }
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot diff " + reference + " and "
-                                       + observed, e);
-               }
-               return diffs;
-       }
-
-       /** Builds a property relPath to be used in the diff. */
-       private static String propertyRelPath(String baseRelPath,
-                       String propertyName) {
-               if (baseRelPath == null)
-                       return propertyName;
-               else
-                       return baseRelPath + '/' + propertyName;
-       }
-
-       /**
-        * Normalizes a name so that it can be stored in contexts not supporting
-        * names with ':' (typically databases). Replaces ':' by '_'.
-        */
-       public static String normalize(String name) {
-               return name.replace(':', '_');
-       }
-
-       /**
-        * Replaces characters which are invalid in a JCR name by '_'. Currently not
-        * exhaustive.
-        * 
-        * @see JcrUtils#INVALID_NAME_CHARACTERS
-        */
-       public static String replaceInvalidChars(String name) {
-               return replaceInvalidChars(name, '_');
-       }
-
-       /**
-        * Replaces characters which are invalid in a JCR name. Currently not
-        * exhaustive.
-        * 
-        * @see JcrUtils#INVALID_NAME_CHARACTERS
-        */
-       public static String replaceInvalidChars(String name, char replacement) {
-               boolean modified = false;
-               char[] arr = name.toCharArray();
-               for (int i = 0; i < arr.length; i++) {
-                       char c = arr[i];
-                       invalid: for (char invalid : INVALID_NAME_CHARACTERS) {
-                               if (c == invalid) {
-                                       arr[i] = replacement;
-                                       modified = true;
-                                       break invalid;
-                               }
-                       }
-               }
-               if (modified)
-                       return new String(arr);
-               else
-                       // do not create new object if unnecessary
-                       return name;
-       }
-
-       /**
-        * Removes forbidden characters from a path, replacing them with '_'
-        * 
-        * @deprecated use {@link #replaceInvalidChars(String)} instead
-        */
-       public static String removeForbiddenCharacters(String str) {
-               return str.replace('[', '_').replace(']', '_').replace('/', '_')
-                               .replace('*', '_');
-
-       }
-
-       /** Cleanly disposes a {@link Binary} even if it is null. */
-       public static void closeQuietly(Binary binary) {
-               if (binary == null)
-                       return;
-               binary.dispose();
-       }
-
-       /** Retrieve a {@link Binary} as a byte array */
-       public static byte[] getBinaryAsBytes(Property property) {
-               ByteArrayOutputStream out = new ByteArrayOutputStream();
-               InputStream in = null;
-               Binary binary = null;
-               try {
-                       binary = property.getBinary();
-                       in = binary.getStream();
-                       IOUtils.copy(in, out);
-                       return out.toByteArray();
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot read binary " + property
-                                       + " as bytes", e);
-               } finally {
-                       IOUtils.closeQuietly(out);
-                       IOUtils.closeQuietly(in);
-                       closeQuietly(binary);
-               }
-       }
-
-       /** Writes a {@link Binary} from a byte array */
-       public static void setBinaryAsBytes(Node node, String property, byte[] bytes) {
-               InputStream in = null;
-               Binary binary = null;
-               try {
-                       in = new ByteArrayInputStream(bytes);
-                       binary = node.getSession().getValueFactory().createBinary(in);
-                       node.setProperty(property, binary);
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot read binary " + property
-                                       + " as bytes", e);
-               } finally {
-                       IOUtils.closeQuietly(in);
-                       closeQuietly(binary);
-               }
-       }
-
-       /**
-        * Creates depth from a string (typically a username) by adding levels based
-        * on its first characters: "aBcD",2 => a/aB
-        */
-       public static String firstCharsToPath(String str, Integer nbrOfChars) {
-               if (str.length() < nbrOfChars)
-                       throw new ArgeoJcrException("String " + str
-                                       + " length must be greater or equal than " + nbrOfChars);
-               StringBuffer path = new StringBuffer("");
-               StringBuffer curr = new StringBuffer("");
-               for (int i = 0; i < nbrOfChars; i++) {
-                       curr.append(str.charAt(i));
-                       path.append(curr);
-                       if (i < nbrOfChars - 1)
-                               path.append('/');
-               }
-               return path.toString();
-       }
-
-       /**
-        * Discards the current changes in the session attached to this node. To be
-        * used typically in a catch block.
-        * 
-        * @see #discardQuietly(Session)
-        */
-       public static void discardUnderlyingSessionQuietly(Node node) {
-               try {
-                       discardQuietly(node.getSession());
-               } catch (RepositoryException e) {
-                       log.warn("Cannot quietly discard session of node " + node + ": "
-                                       + e.getMessage());
-               }
-       }
-
-       /**
-        * Discards the current changes in a session by calling
-        * {@link Session#refresh(boolean)} with <code>false</code>, only logging
-        * potential errors when doing so. To be used typically in a catch block.
-        */
-       public static void discardQuietly(Session session) {
-               try {
-                       if (session != null)
-                               session.refresh(false);
-               } catch (RepositoryException e) {
-                       log.warn("Cannot quietly discard session " + session + ": "
-                                       + e.getMessage());
-               }
-       }
-
-       /**
-        * Login to a workspace with implicit credentials, creates the workspace
-        * with these credentials if it does not already exist.
-        */
-       public static Session loginOrCreateWorkspace(Repository repository,
-                       String workspaceName) throws RepositoryException {
-               Session workspaceSession = null;
-               Session defaultSession = null;
-               try {
-                       try {
-                               workspaceSession = repository.login(workspaceName);
-                       } catch (NoSuchWorkspaceException e) {
-                               // try to create workspace
-                               defaultSession = repository.login();
-                               defaultSession.getWorkspace().createWorkspace(workspaceName);
-                               workspaceSession = repository.login(workspaceName);
-                       }
-                       return workspaceSession;
-               } finally {
-                       logoutQuietly(defaultSession);
-               }
-       }
-
-       /** Logs out the session, not throwing any exception, even if it is null. */
-       public static void logoutQuietly(Session session) {
-               try {
-                       if (session != null)
-                               if (session.isLive())
-                                       session.logout();
-               } catch (Exception e) {
-                       // silent
-               }
-       }
-
-       /**
-        * Convenient method to add a listener. uuids passed as null, deep=true,
-        * local=true, only one node type
-        */
-       public static void addListener(Session session, EventListener listener,
-                       int eventTypes, String basePath, String nodeType) {
-               try {
-                       session.getWorkspace()
-                                       .getObservationManager()
-                                       .addEventListener(
-                                                       listener,
-                                                       eventTypes,
-                                                       basePath,
-                                                       true,
-                                                       null,
-                                                       nodeType == null ? null : new String[] { nodeType },
-                                                       true);
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot add JCR listener " + listener
-                                       + " to session " + session, e);
-               }
-       }
-
-       /** Removes a listener without throwing exception */
-       public static void removeListenerQuietly(Session session,
-                       EventListener listener) {
-               if (session == null || !session.isLive())
-                       return;
-               try {
-                       session.getWorkspace().getObservationManager()
-                                       .removeEventListener(listener);
-               } catch (RepositoryException e) {
-                       // silent
-               }
-       }
-
-       /**
-        * Quietly unregisters an {@link EventListener} from the udnerlying
-        * workspace of this node.
-        */
-       public static void unregisterQuietly(Node node, EventListener eventListener) {
-               try {
-                       unregisterQuietly(node.getSession().getWorkspace(), eventListener);
-               } catch (RepositoryException e) {
-                       // silent
-                       if (log.isTraceEnabled())
-                               log.trace("Could not unregister event listener "
-                                               + eventListener);
-               }
-       }
-
-       /** Quietly unregisters an {@link EventListener} from this workspace */
-       public static void unregisterQuietly(Workspace workspace,
-                       EventListener eventListener) {
-               if (eventListener == null)
-                       return;
-               try {
-                       workspace.getObservationManager()
-                                       .removeEventListener(eventListener);
-               } catch (RepositoryException e) {
-                       // silent
-                       if (log.isTraceEnabled())
-                               log.trace("Could not unregister event listener "
-                                               + eventListener);
-               }
-       }
-
-       /**
-        * If this node is has the {@link NodeType#MIX_LAST_MODIFIED} mixin, it
-        * updates the {@link Property#JCR_LAST_MODIFIED} property with the current
-        * time and the {@link Property#JCR_LAST_MODIFIED_BY} property with the
-        * underlying session user id. In Jackrabbit 2.x, <a
-        * href="https://issues.apache.org/jira/browse/JCR-2233">these properties
-        * are not automatically updated</a>, hence the need for manual update. The
-        * session is not saved.
-        */
-       public static void updateLastModified(Node node) {
-               try {
-                       if (!node.isNodeType(NodeType.MIX_LAST_MODIFIED))
-                               node.addMixin(NodeType.MIX_LAST_MODIFIED);
-                       node.setProperty(Property.JCR_LAST_MODIFIED,
-                                       new GregorianCalendar());
-                       node.setProperty(Property.JCR_LAST_MODIFIED_BY, node.getSession()
-                                       .getUserID());
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot update last modified on " + node,
-                                       e);
-               }
-       }
-
-       /**
-        * Update lastModified recursively until this parent.
-        * 
-        * @param node
-        *            the node
-        * @param untilPath
-        *            the base path, null is equivalent to "/"
-        */
-       public static void updateLastModifiedAndParents(Node node, String untilPath) {
-               try {
-                       if (untilPath != null && !node.getPath().startsWith(untilPath))
-                               throw new ArgeoJcrException(node + " is not under " + untilPath);
-                       updateLastModified(node);
-                       if (untilPath == null) {
-                               if (!node.getPath().equals("/"))
-                                       updateLastModifiedAndParents(node.getParent(), untilPath);
-                       } else {
-                               if (!node.getPath().equals(untilPath))
-                                       updateLastModifiedAndParents(node.getParent(), untilPath);
-                       }
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot update lastModified from " + node
-                                       + " until " + untilPath, e);
-               }
-       }
-
-       /**
-        * Returns a String representing the short version (see <a
-        * href="http://jackrabbit.apache.org/node-type-notation.html"> Node type
-        * Notation </a> attributes grammar) of the main business attributes of this
-        * property definition
-        * 
-        * @param prop
-        */
-       public static String getPropertyDefinitionAsString(Property prop) {
-               StringBuffer sbuf = new StringBuffer();
-               try {
-                       if (prop.getDefinition().isAutoCreated())
-                               sbuf.append("a");
-                       if (prop.getDefinition().isMandatory())
-                               sbuf.append("m");
-                       if (prop.getDefinition().isProtected())
-                               sbuf.append("p");
-                       if (prop.getDefinition().isMultiple())
-                               sbuf.append("*");
-               } catch (RepositoryException re) {
-                       throw new ArgeoJcrException(
-                                       "unexpected error while getting property definition as String",
-                                       re);
-               }
-               return sbuf.toString();
-       }
-
-       /**
-        * Estimate the sub tree size from current node. Computation is based on the
-        * Jcr {@link Property.getLength()} method. Note : it is not the exact size
-        * used on the disk by the current part of the JCR Tree.
-        */
-
-       public static long getNodeApproxSize(Node node) {
-               long curNodeSize = 0;
-               try {
-                       PropertyIterator pi = node.getProperties();
-                       while (pi.hasNext()) {
-                               Property prop = pi.nextProperty();
-                               if (prop.isMultiple()) {
-                                       int nb = prop.getLengths().length;
-                                       for (int i = 0; i < nb; i++) {
-                                               curNodeSize += (prop.getLengths()[i] > 0 ? prop
-                                                               .getLengths()[i] : 0);
-                                       }
-                               } else
-                                       curNodeSize += (prop.getLength() > 0 ? prop.getLength() : 0);
-                       }
-
-                       NodeIterator ni = node.getNodes();
-                       while (ni.hasNext())
-                               curNodeSize += getNodeApproxSize(ni.nextNode());
-                       return curNodeSize;
-               } catch (RepositoryException re) {
-                       throw new ArgeoJcrException(
-                                       "Unexpected error while recursively determining node size.",
-                                       re);
-               }
-       }
-
-       /*
-        * SECURITY
-        */
-
-       /**
-        * Convenience method for adding a single privilege to a principal (user or
-        * role), typically jcr:all
-        */
-       public synchronized static void addPrivilege(Session session, String path,
-                       String principal, String privilege) throws RepositoryException {
-               List<Privilege> privileges = new ArrayList<Privilege>();
-               privileges.add(session.getAccessControlManager().privilegeFromName(
-                               privilege));
-               addPrivileges(session, path, new SimplePrincipal(principal), privileges);
-       }
-
-       /**
-        * Add privileges on a path to a {@link Principal}. The path must already
-        * exist. Session is saved. Synchronized to prevent concurrent modifications
-        * of the same node.
-        */
-       public synchronized static Boolean addPrivileges(Session session,
-                       String path, Principal principal, List<Privilege> privs)
-                       throws RepositoryException {
-               // make sure the session is in line with the persisted state
-               session.refresh(false);
-               AccessControlManager acm = session.getAccessControlManager();
-               AccessControlList acl = getAccessControlList(acm, path);
-
-               accessControlEntries: for (AccessControlEntry ace : acl
-                               .getAccessControlEntries()) {
-                       Principal currentPrincipal = ace.getPrincipal();
-                       if (currentPrincipal.getName().equals(principal.getName())) {
-                               Privilege[] currentPrivileges = ace.getPrivileges();
-                               if (currentPrivileges.length != privs.size())
-                                       break accessControlEntries;
-                               for (int i = 0; i < currentPrivileges.length; i++) {
-                                       Privilege currP = currentPrivileges[i];
-                                       Privilege p = privs.get(i);
-                                       if (!currP.getName().equals(p.getName())) {
-                                               break accessControlEntries;
-                                       }
-                               }
-                               return false;
-                       }
-               }
-
-               Privilege[] privileges = privs.toArray(new Privilege[privs.size()]);
-               acl.addAccessControlEntry(principal, privileges);
-               acm.setPolicy(path, acl);
-               if (log.isDebugEnabled()) {
-                       StringBuffer privBuf = new StringBuffer();
-                       for (Privilege priv : privs)
-                               privBuf.append(priv.getName());
-                       log.debug("Added privileges " + privBuf + " to "
-                                       + principal.getName() + " on " + path + " in '"
-                                       + session.getWorkspace().getName() + "'");
-               }
-               session.refresh(true);
-               session.save();
-               return true;
-       }
-
-       /** Gets access control list for this path, throws exception if not found */
-       public synchronized static AccessControlList getAccessControlList(
-                       AccessControlManager acm, String path) throws RepositoryException {
-               // search for an access control list
-               AccessControlList acl = null;
-               AccessControlPolicyIterator policyIterator = acm
-                               .getApplicablePolicies(path);
-               if (policyIterator.hasNext()) {
-                       while (policyIterator.hasNext()) {
-                               AccessControlPolicy acp = policyIterator
-                                               .nextAccessControlPolicy();
-                               if (acp instanceof AccessControlList)
-                                       acl = ((AccessControlList) acp);
-                       }
-               } else {
-                       AccessControlPolicy[] existingPolicies = acm.getPolicies(path);
-                       for (AccessControlPolicy acp : existingPolicies) {
-                               if (acp instanceof AccessControlList)
-                                       acl = ((AccessControlList) acp);
-                       }
-               }
-               if (acl != null)
-                       return acl;
-               else
-                       throw new ArgeoJcrException("ACL not found at " + path);
-       }
-
-       /** Clear authorizations for a user at this path */
-       public synchronized static void clearAccessControList(Session session,
-                       String path, String username) throws RepositoryException {
-               AccessControlManager acm = session.getAccessControlManager();
-               AccessControlList acl = getAccessControlList(acm, path);
-               for (AccessControlEntry ace : acl.getAccessControlEntries()) {
-                       if (ace.getPrincipal().getName().equals(username)) {
-                               acl.removeAccessControlEntry(ace);
-                       }
-               }
-               // the new access control list must be applied otherwise this call:
-               // acl.removeAccessControlEntry(ace); has no effect
-               acm.setPolicy(path, acl);
-       }
-
-       /*
-        * FILES UTILITIES
-        */
-       /**
-        * Creates the nodes making the path as {@link NodeType#NT_FOLDER}
-        */
-       public static Node mkfolders(Session session, String path) {
-               return mkdirs(session, path, NodeType.NT_FOLDER, NodeType.NT_FOLDER,
-                               false);
-       }
-
-       /**
-        * Copy only nt:folder and nt:file, without their additional types and
-        * properties.
-        * 
-        * @param recursive
-        *            if true copies folders as well, otherwise only first level
-        *            files
-        * @return how many files were copied
-        */
-       @SuppressWarnings("deprecation")
-       public static Long copyFiles(Node fromNode, Node toNode, Boolean recursive,
-                       ArgeoMonitor monitor) {
-               long count = 0l;
-
-               Binary binary = null;
-               InputStream in = null;
-               try {
-                       NodeIterator fromChildren = fromNode.getNodes();
-                       while (fromChildren.hasNext()) {
-                               if (monitor != null && monitor.isCanceled())
-                                       throw new ArgeoJcrException(
-                                                       "Copy cancelled before it was completed");
-
-                               Node fromChild = fromChildren.nextNode();
-                               String fileName = fromChild.getName();
-                               if (fromChild.isNodeType(NodeType.NT_FILE)) {
-                                       if (monitor != null)
-                                               monitor.subTask("Copy " + fileName);
-                                       binary = fromChild.getNode(Node.JCR_CONTENT)
-                                                       .getProperty(Property.JCR_DATA).getBinary();
-                                       in = binary.getStream();
-                                       copyStreamAsFile(toNode, fileName, in);
-                                       IOUtils.closeQuietly(in);
-                                       closeQuietly(binary);
-
-                                       // save session
-                                       toNode.getSession().save();
-                                       count++;
-
-                                       if (log.isDebugEnabled())
-                                               log.debug("Copied file " + fromChild.getPath());
-                                       if (monitor != null)
-                                               monitor.worked(1);
-                               } else if (fromChild.isNodeType(NodeType.NT_FOLDER)
-                                               && recursive) {
-                                       Node toChildFolder;
-                                       if (toNode.hasNode(fileName)) {
-                                               toChildFolder = toNode.getNode(fileName);
-                                               if (!toChildFolder.isNodeType(NodeType.NT_FOLDER))
-                                                       throw new ArgeoJcrException(toChildFolder
-                                                                       + " is not of type nt:folder");
-                                       } else {
-                                               toChildFolder = toNode.addNode(fileName,
-                                                               NodeType.NT_FOLDER);
-
-                                               // save session
-                                               toNode.getSession().save();
-                                       }
-                                       count = count
-                                                       + copyFiles(fromChild, toChildFolder, recursive,
-                                                                       monitor);
-                               }
-                       }
-                       return count;
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot copy files between " + fromNode
-                                       + " and " + toNode);
-               } finally {
-                       // in case there was an exception
-                       IOUtils.closeQuietly(in);
-                       closeQuietly(binary);
-               }
-       }
-
-       /**
-        * Iteratively count all file nodes in subtree, inefficient but can be
-        * useful when query are poorly supported, such as in remoting.
-        */
-       public static Long countFiles(Node node) {
-               Long localCount = 0l;
-               try {
-                       for (NodeIterator nit = node.getNodes(); nit.hasNext();) {
-                               Node child = nit.nextNode();
-                               if (child.isNodeType(NodeType.NT_FOLDER))
-                                       localCount = localCount + countFiles(child);
-                               else if (child.isNodeType(NodeType.NT_FILE))
-                                       localCount = localCount + 1;
-                       }
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot count all children of " + node);
-               }
-               return localCount;
-       }
-
-       /**
-        * Copy a file as an nt:file, assuming an nt:folder hierarchy. The session
-        * is NOT saved.
-        * 
-        * @return the created file node
-        */
-       public static Node copyFile(Node folderNode, File file) {
-               InputStream in = null;
-               try {
-                       in = new FileInputStream(file);
-                       return copyStreamAsFile(folderNode, file.getName(), in);
-               } catch (IOException e) {
-                       throw new ArgeoJcrException("Cannot copy file " + file + " under "
-                                       + folderNode, e);
-               } finally {
-                       IOUtils.closeQuietly(in);
-               }
-       }
-
-       /** Copy bytes as an nt:file */
-       public static Node copyBytesAsFile(Node folderNode, String fileName,
-                       byte[] bytes) {
-               InputStream in = null;
-               try {
-                       in = new ByteArrayInputStream(bytes);
-                       return copyStreamAsFile(folderNode, fileName, in);
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot copy file " + fileName + " under "
-                                       + folderNode, e);
-               } finally {
-                       IOUtils.closeQuietly(in);
-               }
-       }
-
-       /**
-        * Copy a stream as an nt:file, assuming an nt:folder hierarchy. The session
-        * is NOT saved.
-        * 
-        * @return the created file node
-        */
-       public static Node copyStreamAsFile(Node folderNode, String fileName,
-                       InputStream in) {
-               Binary binary = null;
-               try {
-                       Node fileNode;
-                       Node contentNode;
-                       if (folderNode.hasNode(fileName)) {
-                               fileNode = folderNode.getNode(fileName);
-                               if (!fileNode.isNodeType(NodeType.NT_FILE))
-                                       throw new ArgeoJcrException(fileNode
-                                                       + " is not of type nt:file");
-                               // we assume that the content node is already there
-                               contentNode = fileNode.getNode(Node.JCR_CONTENT);
-                       } else {
-                               fileNode = folderNode.addNode(fileName, NodeType.NT_FILE);
-                               contentNode = fileNode.addNode(Node.JCR_CONTENT,
-                                               NodeType.NT_RESOURCE);
-                       }
-                       binary = contentNode.getSession().getValueFactory()
-                                       .createBinary(in);
-                       contentNode.setProperty(Property.JCR_DATA, binary);
-                       return fileNode;
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot create file node " + fileName
-                                       + " under " + folderNode, e);
-               } finally {
-                       closeQuietly(binary);
-               }
-       }
-
-       /** Computes the checksum of an nt:file */
-       public static String checksumFile(Node fileNode, String algorithm) {
-               Binary data = null;
-               InputStream in = null;
-               try {
-                       data = fileNode.getNode(Node.JCR_CONTENT)
-                                       .getProperty(Property.JCR_DATA).getBinary();
-                       in = data.getStream();
-                       return DigestUtils.digest(algorithm, in);
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot checksum file " + fileNode, e);
-               } finally {
-                       IOUtils.closeQuietly(in);
-                       closeQuietly(data);
-               }
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/PropertyDiff.java b/org.argeo.server.jcr/src/org/argeo/jcr/PropertyDiff.java
deleted file mode 100644 (file)
index a0ff471..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import javax.jcr.Value;
-
-/** The result of the comparison of two JCR properties. */
-public class PropertyDiff {
-       public final static Integer MODIFIED = 0;
-       public final static Integer ADDED = 1;
-       public final static Integer REMOVED = 2;
-
-       private final Integer type;
-       private final String relPath;
-       private final Value referenceValue;
-       private final Value newValue;
-
-       public PropertyDiff(Integer type, String relPath, Value referenceValue,
-                       Value newValue) {
-               super();
-
-               if (type == MODIFIED) {
-                       if (referenceValue == null || newValue == null)
-                               throw new ArgeoJcrException(
-                                               "Reference and new values must be specified.");
-               } else if (type == ADDED) {
-                       if (referenceValue != null || newValue == null)
-                               throw new ArgeoJcrException(
-                                               "New value and only it must be specified.");
-               } else if (type == REMOVED) {
-                       if (referenceValue == null || newValue != null)
-                               throw new ArgeoJcrException(
-                                               "Reference value and only it must be specified.");
-               } else {
-                       throw new ArgeoJcrException("Unkown diff type " + type);
-               }
-
-               if (relPath == null)
-                       throw new ArgeoJcrException("Relative path must be specified");
-
-               this.type = type;
-               this.relPath = relPath;
-               this.referenceValue = referenceValue;
-               this.newValue = newValue;
-       }
-
-       public Integer getType() {
-               return type;
-       }
-
-       public String getRelPath() {
-               return relPath;
-       }
-
-       public Value getReferenceValue() {
-               return referenceValue;
-       }
-
-       public Value getNewValue() {
-               return newValue;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/RepositoryRegister.java b/org.argeo.server.jcr/src/org/argeo/jcr/RepositoryRegister.java
deleted file mode 100644 (file)
index 2e3d455..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import java.util.Map;
-
-import javax.jcr.Repository;
-import javax.jcr.RepositoryFactory;
-
-/** Allows to register repositories by name. */
-public interface RepositoryRegister extends RepositoryFactory {
-       /**
-        * The registered {@link Repository} as a read-only map. Note that this
-        * method should be called for each access in order to be sure to be up to
-        * date in case repositories have registered/unregistered
-        */
-       public Map<String, Repository> getRepositories();
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ThreadBoundJcrSessionFactory.java b/org.argeo.server.jcr/src/org/argeo/jcr/ThreadBoundJcrSessionFactory.java
deleted file mode 100644 (file)
index 281bd01..0000000
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import javax.jcr.LoginException;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.SimpleCredentials;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/** Proxy JCR sessions and attach them to calling threads. */
-@Deprecated
-public abstract class ThreadBoundJcrSessionFactory {
-       private final static Log log = LogFactory.getLog(ThreadBoundJcrSessionFactory.class);
-
-       private Repository repository;
-       /** can be injected as list, only used if repository is null */
-       private List<Repository> repositories;
-
-       private ThreadLocal<Session> session = new ThreadLocal<Session>();
-       private final Session proxiedSession;
-       /** If workspace is null, default will be used. */
-       private String workspace = null;
-
-       private String defaultUsername = "demo";
-       private String defaultPassword = "demo";
-       private Boolean forceDefaultCredentials = false;
-
-       private boolean active = true;
-
-       // monitoring
-       private final List<Thread> threads = Collections.synchronizedList(new ArrayList<Thread>());
-       private final Map<Long, Session> activeSessions = Collections.synchronizedMap(new HashMap<Long, Session>());
-       private MonitoringThread monitoringThread;
-
-       public ThreadBoundJcrSessionFactory() {
-               Class<?>[] interfaces = { Session.class };
-               proxiedSession = (Session) Proxy.newProxyInstance(ThreadBoundJcrSessionFactory.class.getClassLoader(),
-                               interfaces, new JcrSessionInvocationHandler());
-       }
-
-       /** Logs in to the repository using various strategies. */
-       protected synchronized Session login() {
-               if (!isActive())
-                       throw new ArgeoJcrException("Thread bound session factory inactive");
-
-               // discard session previously attached to this thread
-               Thread thread = Thread.currentThread();
-               if (activeSessions.containsKey(thread.getId())) {
-                       Session oldSession = activeSessions.remove(thread.getId());
-                       oldSession.logout();
-                       session.remove();
-               }
-
-               Session newSession = null;
-               // first try to login without credentials, assuming the underlying login
-               // module will have dealt with authentication (typically using Spring
-               // Security)
-               if (!forceDefaultCredentials)
-                       try {
-                               newSession = repository().login(workspace);
-                       } catch (LoginException e1) {
-                               log.warn("Cannot login without credentials: " + e1.getMessage());
-                               // invalid credentials, go to the next step
-                       } catch (RepositoryException e1) {
-                               // other kind of exception, fail
-                               throw new ArgeoJcrException("Cannot log in to repository", e1);
-                       }
-
-               // log using default username / password (useful for testing purposes)
-               if (newSession == null)
-                       try {
-                               SimpleCredentials sc = new SimpleCredentials(defaultUsername, defaultPassword.toCharArray());
-                               newSession = repository().login(sc, workspace);
-                       } catch (RepositoryException e) {
-                               throw new ArgeoJcrException("Cannot log in to repository", e);
-                       }
-
-               session.set(newSession);
-               // Log and monitor new session
-               if (log.isTraceEnabled())
-                       log.trace("Logged in to JCR session " + newSession + "; userId=" + newSession.getUserID());
-
-               // monitoring
-               activeSessions.put(thread.getId(), newSession);
-               threads.add(thread);
-               return newSession;
-       }
-
-       public Object getObject() {
-               return proxiedSession;
-       }
-
-       public void init() throws Exception {
-               // log.error("SHOULD NOT BE USED ANYMORE");
-               monitoringThread = new MonitoringThread();
-               monitoringThread.start();
-       }
-
-       public void dispose() throws Exception {
-               // if (activeSessions.size() == 0)
-               // return;
-
-               if (log.isTraceEnabled())
-                       log.trace("Cleaning up " + activeSessions.size() + " active JCR sessions...");
-
-               deactivate();
-               for (Session sess : activeSessions.values()) {
-                       JcrUtils.logoutQuietly(sess);
-               }
-               activeSessions.clear();
-       }
-
-       protected Boolean isActive() {
-               return active;
-       }
-
-       protected synchronized void deactivate() {
-               active = false;
-               notifyAll();
-       }
-
-       protected synchronized void removeSession(Thread thread) {
-               if (!isActive())
-                       return;
-               activeSessions.remove(thread.getId());
-               threads.remove(thread);
-       }
-
-       protected synchronized void cleanDeadThreads() {
-               if (!isActive())
-                       return;
-               Iterator<Thread> it = threads.iterator();
-               while (it.hasNext()) {
-                       Thread thread = it.next();
-                       if (!thread.isAlive() && isActive()) {
-                               if (activeSessions.containsKey(thread.getId())) {
-                                       Session session = activeSessions.get(thread.getId());
-                                       activeSessions.remove(thread.getId());
-                                       session.logout();
-                                       if (log.isTraceEnabled())
-                                               log.trace("Cleaned up JCR session (userID=" + session.getUserID() + ") from dead thread "
-                                                               + thread.getId());
-                               }
-                               it.remove();
-                       }
-               }
-               try {
-                       wait(1000);
-               } catch (InterruptedException e) {
-                       // silent
-               }
-       }
-
-       public Class<? extends Session> getObjectType() {
-               return Session.class;
-       }
-
-       public boolean isSingleton() {
-               return true;
-       }
-
-       /**
-        * Called before a method is actually called, allowing to check the session
-        * or re-login it (e.g. if authentication has changed). The default
-        * implementation returns the session.
-        */
-       protected Session preCall(Session session) {
-               return session;
-       }
-
-       protected Repository repository() {
-               if (repository != null)
-                       return repository;
-               if (repositories != null) {
-                       // hardened for OSGi dynamic services
-                       Iterator<Repository> it = repositories.iterator();
-                       if (it.hasNext())
-                               return it.next();
-               }
-               throw new ArgeoJcrException("No repository injected");
-       }
-
-       // /** Useful for declarative registration of OSGi services (blueprint) */
-       // public void register(Repository repository, Map<?, ?> params) {
-       // this.repository = repository;
-       // }
-       //
-       // /** Useful for declarative registration of OSGi services (blueprint) */
-       // public void unregister(Repository repository, Map<?, ?> params) {
-       // this.repository = null;
-       // }
-
-       public void setRepository(Repository repository) {
-               this.repository = repository;
-       }
-
-       public void setRepositories(List<Repository> repositories) {
-               this.repositories = repositories;
-       }
-
-       public void setDefaultUsername(String defaultUsername) {
-               this.defaultUsername = defaultUsername;
-       }
-
-       public void setDefaultPassword(String defaultPassword) {
-               this.defaultPassword = defaultPassword;
-       }
-
-       public void setForceDefaultCredentials(Boolean forceDefaultCredentials) {
-               this.forceDefaultCredentials = forceDefaultCredentials;
-       }
-
-       public void setWorkspace(String workspace) {
-               this.workspace = workspace;
-       }
-
-       protected class JcrSessionInvocationHandler implements InvocationHandler {
-
-               public Object invoke(Object proxy, Method method, Object[] args) throws Throwable, RepositoryException {
-                       Session threadSession = session.get();
-                       if (threadSession == null) {
-                               if ("logout".equals(method.getName()))// no need to login
-                                       return Void.TYPE;
-                               else if ("toString".equals(method.getName()))// maybe logging
-                                       return "Uninitialized Argeo thread bound JCR session";
-                               threadSession = login();
-                       }
-
-                       preCall(threadSession);
-                       Object ret;
-                       try {
-                               ret = method.invoke(threadSession, args);
-                       } catch (InvocationTargetException e) {
-                               Throwable cause = e.getCause();
-                               if (cause instanceof RepositoryException)
-                                       throw (RepositoryException) cause;
-                               else
-                                       throw cause;
-                       }
-                       if ("logout".equals(method.getName())) {
-                               session.remove();
-                               Thread thread = Thread.currentThread();
-                               removeSession(thread);
-                               if (log.isTraceEnabled())
-                                       log.trace("Logged out JCR session (userId=" + threadSession.getUserID() + ") on thread "
-                                                       + thread.getId());
-                       }
-                       return ret;
-               }
-       }
-
-       /** Monitors registered thread in order to clean up dead ones. */
-       private class MonitoringThread extends Thread {
-
-               public MonitoringThread() {
-                       super("ThreadBound JCR Session Monitor");
-               }
-
-               @Override
-               public void run() {
-                       while (isActive()) {
-                               cleanDeadThreads();
-                       }
-               }
-
-       }
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/UserJcrUtils.java b/org.argeo.server.jcr/src/org/argeo/jcr/UserJcrUtils.java
deleted file mode 100644 (file)
index 9526947..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.query.Query;
-import javax.jcr.query.qom.Constraint;
-import javax.jcr.query.qom.DynamicOperand;
-import javax.jcr.query.qom.QueryObjectModelFactory;
-import javax.jcr.query.qom.Selector;
-import javax.jcr.query.qom.StaticOperand;
-
-/**
- * Utilities related to the user home and properties based on Argeo JCR model.
- * Do not use anymore. Does not fit with current security model
- */
-@Deprecated
-public class UserJcrUtils {
-       /** The home base path. Not yet configurable */
-       public final static String DEFAULT_HOME_BASE_PATH = "/home";
-
-       /**
-        * Returns the home node of the user or null if none was found.
-        * 
-        * @param session
-        *            the session to use in order to perform the search, this can be
-        *            a session with a different user ID than the one searched,
-        *            typically when a system or admin session is used.
-        * @param username
-        *            the username of the user
-        */
-       public static Node getUserHome(Session session, String username) {
-               try {
-                       // String homePath = UserJcrUtils.getUserHomePath(username);
-                       // return session.itemExists(homePath) ? session.getNode(homePath)
-                       // : null;
-                       // kept for example of QOM queries
-                       QueryObjectModelFactory qomf = session.getWorkspace()
-                                       .getQueryManager().getQOMFactory();
-                       Selector userHomeSel = qomf.selector(ArgeoTypes.ARGEO_USER_HOME,
-                                       "userHome");
-                       DynamicOperand userIdDop = qomf.propertyValue(
-                                       userHomeSel.getSelectorName(), ArgeoNames.ARGEO_USER_ID);
-                       StaticOperand userIdSop = qomf.literal(session.getValueFactory()
-                                       .createValue(username));
-                       Constraint constraint = qomf.comparison(userIdDop,
-                                       QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop);
-                       Query query = qomf.createQuery(userHomeSel, constraint, null, null);
-                       return JcrUtils.querySingleNode(query);
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot find home for user " + username, e);
-               }
-       }
-
-       public static Node getUserProfile(Session session, String username) {
-               try {
-                       QueryObjectModelFactory qomf = session.getWorkspace()
-                                       .getQueryManager().getQOMFactory();
-                       Selector userHomeSel = qomf.selector(ArgeoTypes.ARGEO_USER_PROFILE,
-                                       "userProfile");
-                       DynamicOperand userIdDop = qomf.propertyValue(
-                                       userHomeSel.getSelectorName(), ArgeoNames.ARGEO_USER_ID);
-                       StaticOperand userIdSop = qomf.literal(session.getValueFactory()
-                                       .createValue(username));
-                       Constraint constraint = qomf.comparison(userIdDop,
-                                       QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop);
-                       Query query = qomf.createQuery(userHomeSel, constraint, null, null);
-                       return JcrUtils.querySingleNode(query);
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException(
-                                       "Cannot find profile for user " + username, e);
-               }
-       }
-
-       /** Returns the home node of the session user or null if none was found. */
-       public static Node getUserHome(Session session) {
-               String userID = session.getUserID();
-               return getUserHome(session, userID);
-       }
-
-       private UserJcrUtils() {
-       }
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/VersionDiff.java b/org.argeo.server.jcr/src/org/argeo/jcr/VersionDiff.java
deleted file mode 100644 (file)
index e6ae913..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr;
-
-import java.util.Calendar;
-import java.util.Map;
-
-/**
- * Generic Object that enables the creation of history reports based on a JCR
- * versionable node. userId and creation date are added to the map of
- * PropertyDiff.
- * 
- * These two fields might be null
- * 
- */
-public class VersionDiff {
-
-       private String userId;
-       private Map<String, PropertyDiff> diffs;
-       private Calendar updateTime;
-
-       public VersionDiff(String userId, Calendar updateTime,
-                       Map<String, PropertyDiff> diffs) {
-               this.userId = userId;
-               this.updateTime = updateTime;
-               this.diffs = diffs;
-       }
-
-       public String getUserId() {
-               return userId;
-       }
-
-       public Map<String, PropertyDiff> getDiffs() {
-               return diffs;
-       }
-
-       public Calendar getUpdateTime() {
-               return updateTime;
-       }
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/argeo.cnd b/org.argeo.server.jcr/src/org/argeo/jcr/argeo.cnd
deleted file mode 100644 (file)
index fbfea9d..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<argeo = 'http://www.argeo.org/ns/argeo'>
-
-// GENERIC TYPES NOT AVAILABLE IN JCR
-[argeo:link] > mix:created, mix:lastModified
-mixin
-// URI(s)
-- argeo:uri (STRING) m
-
-[argeo:references] > nt:unstructured
-- * (REFERENCE) *
-
-// DATA MODEL
-[argeo:dataModel] > mix:created, mix:lastModified, mix:versionable
-mixin
-- argeo:uri (STRING) m
-- argeo:dataModelVersion (STRING) m
-
-// USER NODES
-// user should be lower case, between 3 and 15 characters long
-[argeo:userHome] > mix:created, mix:lastModified
-mixin
-- argeo:userID (STRING) m
-- argeo:remoteRoles (STRING) *
-// deprecated. for backward compatibility:
-+ argeo:profile (argeo:userProfile)
-+ argeo:keyring (argeo:pbeSpec)
-+ argeo:preferences (argeo:preferenceNode)
-
-[argeo:userProfile] > mix:created, mix:lastModified, mix:title, mix:versionable
-mixin
-- argeo:userID (STRING) m
-- argeo:enabled (BOOLEAN)
-- argeo:accountNonExpired (BOOLEAN)
-- argeo:accountNonLocked (BOOLEAN)
-- argeo:credentialsNonExpired (BOOLEAN)
-
-[argeo:preferenceNode] > mix:lastModified, mix:versionable
-mixin
-+ * (argeo:preferenceNode) * version
-
-[argeo:remoteRepository] > nt:unstructured
-- argeo:uri (STRING)
-- argeo:userID (STRING)
-+ argeo:password (argeo:encrypted)
-
-// TABULAR CONTENT
-[argeo:table] > nt:file
-+ * (argeo:column) *
-
-[argeo:column] > mix:title
-- jcr:requiredType (STRING) = 'STRING'
-
-[argeo:csv] > nt:resource
-
-// CRYPTO
-[argeo:encrypted] > nt:base
-mixin
-// initialization vector used by some algorithms
-- argeo:iv (BINARY)
-
-[argeo:pbeKeySpec] > nt:base
-mixin
-- argeo:secretKeyFactory (STRING)
-- argeo:salt (BINARY)
-- argeo:iterationCount (LONG)
-- argeo:keyLength (LONG)
-- argeo:secretKeyEncryption (STRING)
-
-[argeo:pbeSpec] > argeo:pbeKeySpec
-mixin
-- argeo:cipher (STRING)
-
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/docbook/DocBookModel.java b/org.argeo.server.jcr/src/org/argeo/jcr/docbook/DocBookModel.java
deleted file mode 100644 (file)
index 61a902d..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-package org.argeo.jcr.docbook;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import javax.jcr.ImportUUIDBehavior;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-public class DocBookModel {
-       private final static Log log = LogFactory.getLog(DocBookModel.class);
-       private Session session;
-
-       public DocBookModel(Session session) {
-               super();
-               this.session = session;
-       }
-
-       public void setSession(Session session) {
-               this.session = session;
-       }
-
-       public void importXml(String path, InputStream in)
-                       throws RepositoryException, IOException {
-               long begin = System.currentTimeMillis();
-               session.importXML(path, in,
-                               ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
-               long duration = System.currentTimeMillis() - begin;
-               if (log.isTraceEnabled())
-                       log.trace("Imported " + path + " in " + duration + " ms");
-
-       }
-
-       public void exportXml(String path, OutputStream out)
-                       throws RepositoryException, IOException {
-               session.exportDocumentView(path, out, true, false);
-       }
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/docbook/DocBookNames.java b/org.argeo.server.jcr/src/org/argeo/jcr/docbook/DocBookNames.java
deleted file mode 100644 (file)
index f4edb12..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.argeo.jcr.docbook;
-
-public interface DocBookNames {
-       public final static String DBK_ = "dbk:";
-       public final static String DBK_PARA = DBK_ + "para";
-       public final static String DBK_SECTION = DBK_ + "section";
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/docbook/docbook-full.cnd b/org.argeo.server.jcr/src/org/argeo/jcr/docbook/docbook-full.cnd
deleted file mode 100644 (file)
index 7e4fefa..0000000
+++ /dev/null
@@ -1,3005 +0,0 @@
-<dbk = 'http://docbook.org/ns/docbook'>
-<argeodbk = 'http://www.argeo.org/ns/argeodbk'>
-<jcr = 'http://www.jcp.org/jcr/1.0'>
-<nt = 'http://www.jcp.org/jcr/nt/1.0'>
-<xlink = 'http://www.w3.org/1999/xlink'>
-<xs = 'http://www.w3.org/2001/XMLSchema'>
-<xml = 'http://www.w3.org/XML/1998/namespace'>
-
-[argeodbk:titled]
-mixin
- + dbk:info (dbk:info) = dbk:info *
- + dbk:title (dbk:title) = dbk:title *
- + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev *
-
-[argeodbk:linkingAttributes]
-mixin
- - dbk:linkend (String)
- - xlink:actuate (String)
- - xlink:arcrole (String)
- - xlink:href (String)
- - xlink:role (String)
- - xlink:show (String)
- - xlink:title (String)
- - xlink:type (String)
-
-[argeodbk:freeText]
-mixin
- + dbk:phrase (dbk:phrase) = dbk:phrase *
- + dbk:replaceable (dbk:replaceable) = dbk:replaceable *
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[argeodbk:markupInlines]
-mixin
- + dbk:code (dbk:code) = dbk:code *
- + dbk:constant (dbk:constant) = dbk:constant *
- + dbk:email (dbk:email) = dbk:email *
- + dbk:literal (dbk:literal) = dbk:literal *
- + dbk:markup (dbk:markup) = dbk:markup *
- + dbk:symbol (dbk:symbol) = dbk:symbol *
- + dbk:tag (dbk:tag) = dbk:tag *
- + dbk:token (dbk:token) = dbk:token *
- + dbk:uri (dbk:uri) = dbk:uri *
-
-[argeodbk:listElements]
-mixin
- + dbk:bibliolist (dbk:bibliolist) = dbk:bibliolist *
- + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist *
- + dbk:glosslist (dbk:glosslist) = dbk:glosslist *
- + dbk:itemizedlist (dbk:itemizedlist) = dbk:itemizedlist *
- + dbk:orderedlist (dbk:orderedlist) = dbk:orderedlist *
- + dbk:procedure (dbk:procedure) = dbk:procedure *
- + dbk:qandaset (dbk:qandaset) = dbk:qandaset *
- + dbk:segmentedlist (dbk:segmentedlist) = dbk:segmentedlist *
- + dbk:simplelist (dbk:simplelist) = dbk:simplelist *
- + dbk:variablelist (dbk:variablelist) = dbk:variablelist *
-
-[argeodbk:paragraphElements]
-mixin
- + dbk:formalpara (dbk:formalpara) = dbk:formalpara *
- + dbk:para (dbk:para) = dbk:para *
- + dbk:simpara (dbk:simpara) = dbk:simpara *
-
-[argeodbk:indexingInlines]
-mixin
- + dbk:indexterm (dbk:indexterm) = dbk:indexterm *
-
-[argeodbk:techDocElements]
-mixin
- + dbk:caution (dbk:caution) = dbk:caution *
- + dbk:classsynopsis (dbk:classsynopsis) = dbk:classsynopsis *
- + dbk:cmdsynopsis (dbk:cmdsynopsis) = dbk:cmdsynopsis *
- + dbk:constraintdef (dbk:constraintdef) = dbk:constraintdef *
- + dbk:constructorsynopsis (dbk:constructorsynopsis) = dbk:constructorsynopsis *
- + dbk:destructorsynopsis (dbk:destructorsynopsis) = dbk:destructorsynopsis *
- + dbk:equation (dbk:equation) = dbk:equation *
- + dbk:example (dbk:example) = dbk:example *
- + dbk:fieldsynopsis (dbk:fieldsynopsis) = dbk:fieldsynopsis *
- + dbk:figure (dbk:figure) = dbk:figure *
- + dbk:funcsynopsis (dbk:funcsynopsis) = dbk:funcsynopsis *
- + dbk:important (dbk:important) = dbk:important *
- + dbk:informalequation (dbk:informalequation) = dbk:informalequation *
- + dbk:informalexample (dbk:informalexample) = dbk:informalexample *
- + dbk:informalfigure (dbk:informalfigure) = dbk:informalfigure *
- + dbk:informaltable (dbk:informaltable) = dbk:informaltable *
- + dbk:literallayout (dbk:literallayout) = dbk:literallayout *
- + dbk:methodsynopsis (dbk:methodsynopsis) = dbk:methodsynopsis *
- + dbk:msgset (dbk:msgset) = dbk:msgset *
- + dbk:note (dbk:note) = dbk:note *
- + dbk:productionset (dbk:productionset) = dbk:productionset *
- + dbk:programlisting (dbk:programlisting) = dbk:programlisting *
- + dbk:programlistingco (dbk:programlistingco) = dbk:programlistingco *
- + dbk:screen (dbk:screen) = dbk:screen *
- + dbk:screenco (dbk:screenco) = dbk:screenco *
- + dbk:synopsis (dbk:synopsis) = dbk:synopsis *
- + dbk:table (dbk:table) = dbk:table *
- + dbk:task (dbk:task) = dbk:task *
- + dbk:tip (dbk:tip) = dbk:tip *
- + dbk:warning (dbk:warning) = dbk:warning *
-
-[argeodbk:techDocInlines]
-mixin
- + dbk:accel (dbk:accel) = dbk:accel *
- + dbk:application (dbk:application) = dbk:application *
- + dbk:classname (dbk:classname) = dbk:classname *
- + dbk:command (dbk:command) = dbk:command *
- + dbk:computeroutput (dbk:computeroutput) = dbk:computeroutput *
- + dbk:database (dbk:database) = dbk:database *
- + dbk:envar (dbk:envar) = dbk:envar *
- + dbk:errorcode (dbk:errorcode) = dbk:errorcode *
- + dbk:errorname (dbk:errorname) = dbk:errorname *
- + dbk:errortext (dbk:errortext) = dbk:errortext *
- + dbk:errortype (dbk:errortype) = dbk:errortype *
- + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname *
- + dbk:filename (dbk:filename) = dbk:filename *
- + dbk:function (dbk:function) = dbk:function *
- + dbk:guibutton (dbk:guibutton) = dbk:guibutton *
- + dbk:guiicon (dbk:guiicon) = dbk:guiicon *
- + dbk:guilabel (dbk:guilabel) = dbk:guilabel *
- + dbk:guimenu (dbk:guimenu) = dbk:guimenu *
- + dbk:guimenuitem (dbk:guimenuitem) = dbk:guimenuitem *
- + dbk:guisubmenu (dbk:guisubmenu) = dbk:guisubmenu *
- + dbk:hardware (dbk:hardware) = dbk:hardware *
- + dbk:initializer (dbk:initializer) = dbk:initializer *
- + dbk:inlineequation (dbk:inlineequation) = dbk:inlineequation *
- + dbk:interfacename (dbk:interfacename) = dbk:interfacename *
- + dbk:keycap (dbk:keycap) = dbk:keycap *
- + dbk:keycode (dbk:keycode) = dbk:keycode *
- + dbk:keycombo (dbk:keycombo) = dbk:keycombo *
- + dbk:keysym (dbk:keysym) = dbk:keysym *
- + dbk:menuchoice (dbk:menuchoice) = dbk:menuchoice *
- + dbk:methodname (dbk:methodname) = dbk:methodname *
- + dbk:modifier (dbk:modifier) = dbk:modifier *
- + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton *
- + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal *
- + dbk:ooclass (dbk:ooclass) = dbk:ooclass *
- + dbk:ooexception (dbk:ooexception) = dbk:ooexception *
- + dbk:oointerface (dbk:oointerface) = dbk:oointerface *
- + dbk:option (dbk:option) = dbk:option *
- + dbk:optional (dbk:optional) = dbk:optional *
- + dbk:package (dbk:package) = dbk:package *
- + dbk:parameter (dbk:parameter) = dbk:parameter *
- + dbk:productname (dbk:productname) = dbk:productname *
- + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
- + dbk:prompt (dbk:prompt) = dbk:prompt *
- + dbk:property (dbk:property) = dbk:property *
- + dbk:returnvalue (dbk:returnvalue) = dbk:returnvalue *
- + dbk:shortcut (dbk:shortcut) = dbk:shortcut *
- + dbk:systemitem (dbk:systemitem) = dbk:systemitem *
- + dbk:termdef (dbk:termdef) = dbk:termdef *
- + dbk:trademark (dbk:trademark) = dbk:trademark *
- + dbk:type (dbk:type) = dbk:type *
- + dbk:userinput (dbk:userinput) = dbk:userinput *
- + dbk:varname (dbk:varname) = dbk:varname *
-
-[argeodbk:publishingElements]
-mixin
- + dbk:address (dbk:address) = dbk:address *
- + dbk:blockquote (dbk:blockquote) = dbk:blockquote *
- + dbk:epigraph (dbk:epigraph) = dbk:epigraph *
- + dbk:sidebar (dbk:sidebar) = dbk:sidebar *
-
-[argeodbk:ubiquitousInlines]
-mixin
- + dbk:alt (dbk:alt) = dbk:alt *
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:biblioref (dbk:biblioref) = dbk:biblioref *
- + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
- + dbk:link (dbk:link) = dbk:link *
- + dbk:olink (dbk:olink) = dbk:olink *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:subscript (dbk:subscript) = dbk:subscript *
- + dbk:superscript (dbk:superscript) = dbk:superscript *
- + dbk:xref (dbk:xref) = dbk:xref *
-
-[argeodbk:abstractSection]
-mixin
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bibliography (dbk:bibliography) = dbk:bibliography *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:glossary (dbk:glossary) = dbk:glossary *
- + dbk:index (dbk:index) = dbk:index *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- + dbk:toc (dbk:toc) = dbk:toc *
- - dbk:label (String)
- - dbk:status (String)
-
-[argeodbk:bibliographyInlines]
-mixin
- + dbk:author (dbk:author) = dbk:author *
- + dbk:citation (dbk:citation) = dbk:citation *
- + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid *
- + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry *
- + dbk:citetitle (dbk:citetitle) = dbk:citetitle *
- + dbk:editor (dbk:editor) = dbk:editor *
- + dbk:jobtitle (dbk:jobtitle) = dbk:jobtitle *
- + dbk:org (dbk:org) = dbk:org *
- + dbk:orgname (dbk:orgname) = dbk:orgname *
- + dbk:person (dbk:person) = dbk:person *
- + dbk:personname (dbk:personname) = dbk:personname *
-
-[argeodbk:publishingInlines]
-mixin
- + dbk:abbrev (dbk:abbrev) = dbk:abbrev *
- + dbk:acronym (dbk:acronym) = dbk:acronym *
- + dbk:coref (dbk:coref) = dbk:coref *
- + dbk:date (dbk:date) = dbk:date *
- + dbk:emphasis (dbk:emphasis) = dbk:emphasis *
- + dbk:firstterm (dbk:firstterm) = dbk:firstterm *
- + dbk:footnote (dbk:footnote) = dbk:footnote *
- + dbk:footnoteref (dbk:footnoteref) = dbk:footnoteref *
- + dbk:foreignphrase (dbk:foreignphrase) = dbk:foreignphrase *
- + dbk:glossterm (dbk:glossterm) = dbk:glossterm *
- + dbk:quote (dbk:quote) = dbk:quote *
- + dbk:wordasword (dbk:wordasword) = dbk:wordasword *
-
-[argeodbk:base]
-abstract
- - dbk:annotations (String)
- - dbk:arch (String)
- - dbk:audience (String)
- - dbk:condition (String)
- - dbk:conformance (String)
- - dbk:dir (String)
- - dbk:os (String)
- - dbk:remap (String)
- - dbk:revision (String)
- - dbk:revisionflag (String)
- - dbk:role (String)
- - dbk:security (String)
- - dbk:userlevel (String)
- - dbk:vendor (String)
- - dbk:version (String)
- - dbk:wordsize (String)
- - dbk:xreflabel (String)
- - xml:base (String)
- - xml:id (String)
- - xml:lang (String)
-
-[dbk:abbrev] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:trademark (dbk:trademark) = dbk:trademark *
-
-[dbk:abstract] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
-
-[dbk:accel] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:acknowledgements] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:acronym] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:trademark (dbk:trademark) = dbk:trademark *
-
-[dbk:address] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:city (dbk:city) = dbk:city *
- + dbk:country (dbk:country) = dbk:country *
- + dbk:email (dbk:email) = dbk:email *
- + dbk:fax (dbk:fax) = dbk:fax *
- + dbk:otheraddr (dbk:otheraddr) = dbk:otheraddr *
- + dbk:personname (dbk:personname) = dbk:personname *
- + dbk:phone (dbk:phone) = dbk:phone *
- + dbk:pob (dbk:pob) = dbk:pob *
- + dbk:postcode (dbk:postcode) = dbk:postcode *
- + dbk:state (dbk:state) = dbk:state *
- + dbk:street (dbk:street) = dbk:street *
- + dbk:uri (dbk:uri) = dbk:uri *
- - dbk:continuation (String) 
- - dbk:language (String) 
- - dbk:linenumbering (String) 
- - dbk:startinglinenumber (String) 
- - xml:space (String) 
-
-[dbk:affiliation] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:address (dbk:address) = dbk:address *
- + dbk:jobtitle (dbk:jobtitle) = dbk:jobtitle *
- + dbk:org (dbk:org) = dbk:org
- + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
- + dbk:orgname (dbk:orgname) = dbk:orgname
- + dbk:shortaffil (dbk:shortaffil) = dbk:shortaffil
-
-[dbk:alt] > argeodbk:base
- + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:anchor] > argeodbk:base
-
-[dbk:annotation] > argeodbk:base, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:annotates (String) 
-
-[dbk:answer] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:label (dbk:label) = dbk:label
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:appendix] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:refentry (dbk:refentry) = dbk:refentry *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:sect1 (dbk:sect1) = dbk:sect1 *
- + dbk:section (dbk:section) = dbk:section *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
-
-[dbk:application] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
-
-[dbk:arc] > argeodbk:base
- - xlink:from (String) 
- - xlink:to (String) 
-
-[dbk:area] > argeodbk:base
- + dbk:alt (dbk:alt) = dbk:alt
- - dbk:coords (String) 
- - dbk:label (String) 
- - dbk:linkends (String) 
- - dbk:otherunits (String) 
- - dbk:units (String) 
-
-[dbk:areaset] > argeodbk:base
- + dbk:area (dbk:area) = dbk:area *
- - dbk:label (String) 
- - dbk:linkends (String) 
- - dbk:otherunits (String) 
- - dbk:units (String) 
-
-[dbk:areaspec] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:area (dbk:area) = dbk:area *
- + dbk:areaset (dbk:areaset) = dbk:areaset *
- - dbk:otherunits (String) 
- - dbk:units (String) 
-
-[dbk:arg] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:arg (dbk:arg) = dbk:arg *
- + dbk:group (dbk:group) = dbk:group *
- + dbk:option (dbk:option) = dbk:option *
- + dbk:sbr (dbk:sbr) = dbk:sbr *
- + dbk:synopfragmentref (dbk:synopfragmentref) = dbk:synopfragmentref *
- - dbk:choice (String) 
- - dbk:rep (String) 
-
-[dbk:article] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:acknowledgements (dbk:acknowledgements) = dbk:acknowledgements *
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:appendix (dbk:appendix) = dbk:appendix *
- + dbk:colophon (dbk:colophon) = dbk:colophon *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:refentry (dbk:refentry) = dbk:refentry *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:sect1 (dbk:sect1) = dbk:sect1 *
- + dbk:section (dbk:section) = dbk:section *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
- - dbk:class (String) 
-
-[dbk:artpagenums] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:attribution] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:citation (dbk:citation) = dbk:citation *
- + dbk:citetitle (dbk:citetitle) = dbk:citetitle *
- + dbk:person (dbk:person) = dbk:person *
- + dbk:personname (dbk:personname) = dbk:personname *
-
-[dbk:audiodata] > argeodbk:base
- + dbk:info (dbk:info) = dbk:info
- - dbk:entityref (String) 
- - dbk:fileref (String) 
- - dbk:format (String) 
-
-[dbk:audioobject] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:audiodata (dbk:audiodata) = dbk:audiodata
- + dbk:info (dbk:info) = dbk:info
-
-[dbk:author] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:address (dbk:address) = dbk:address *
- + dbk:affiliation (dbk:affiliation) = dbk:affiliation *
- + dbk:contrib (dbk:contrib) = dbk:contrib *
- + dbk:email (dbk:email) = dbk:email *
- + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
- + dbk:orgname (dbk:orgname) = dbk:orgname
- + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
- + dbk:personname (dbk:personname) = dbk:personname
- + dbk:uri (dbk:uri) = dbk:uri *
-
-[dbk:authorgroup] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:author (dbk:author) = dbk:author *
- + dbk:editor (dbk:editor) = dbk:editor *
- + dbk:othercredit (dbk:othercredit) = dbk:othercredit *
-
-[dbk:authorinitials] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:bibliocoverage] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:otherspatial (String) 
- - dbk:othertemporal (String) 
- - dbk:spatial (String) 
- - dbk:temporal (String) 
-
-[dbk:bibliodiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:biblioentry (dbk:biblioentry) = dbk:biblioentry *
- + dbk:bibliomixed (dbk:bibliomixed) = dbk:bibliomixed *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:biblioentry] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:publishingInlines
- + dbk:abstract (dbk:abstract) = dbk:abstract *
- + dbk:address (dbk:address) = dbk:address *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums *
- + dbk:author (dbk:author) = dbk:author *
- + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup *
- + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials *
- + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage *
- + dbk:biblioid (dbk:biblioid) = dbk:biblioid *
- + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc *
- + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset *
- + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation *
- + dbk:biblioset (dbk:biblioset) = dbk:biblioset *
- + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource *
- + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid *
- + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry *
- + dbk:citetitle (dbk:citetitle) = dbk:citetitle *
- + dbk:collab (dbk:collab) = dbk:collab *
- + dbk:confgroup (dbk:confgroup) = dbk:confgroup *
- + dbk:contractnum (dbk:contractnum) = dbk:contractnum *
- + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor *
- + dbk:copyright (dbk:copyright) = dbk:copyright *
- + dbk:cover (dbk:cover) = dbk:cover *
- + dbk:edition (dbk:edition) = dbk:edition *
- + dbk:editor (dbk:editor) = dbk:editor *
- + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink *
- + dbk:issuenum (dbk:issuenum) = dbk:issuenum *
- + dbk:itermset (dbk:itermset) = dbk:itermset *
- + dbk:keywordset (dbk:keywordset) = dbk:keywordset *
- + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:org (dbk:org) = dbk:org *
- + dbk:orgname (dbk:orgname) = dbk:orgname *
- + dbk:othercredit (dbk:othercredit) = dbk:othercredit *
- + dbk:pagenums (dbk:pagenums) = dbk:pagenums *
- + dbk:person (dbk:person) = dbk:person *
- + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
- + dbk:personname (dbk:personname) = dbk:personname *
- + dbk:phrase (dbk:phrase) = dbk:phrase *
- + dbk:printhistory (dbk:printhistory) = dbk:printhistory *
- + dbk:productname (dbk:productname) = dbk:productname *
- + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
- + dbk:pubdate (dbk:pubdate) = dbk:pubdate *
- + dbk:publisher (dbk:publisher) = dbk:publisher *
- + dbk:publishername (dbk:publishername) = dbk:publishername *
- + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums *
- + dbk:subjectset (dbk:subjectset) = dbk:subjectset *
- + dbk:subscript (dbk:subscript) = dbk:subscript *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- + dbk:superscript (dbk:superscript) = dbk:superscript *
- + dbk:title (dbk:title) = dbk:title *
- + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev *
- + dbk:volumenum (dbk:volumenum) = dbk:volumenum *
-
-[dbk:bibliography] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bibliodiv (dbk:bibliodiv) = dbk:bibliodiv *
- + dbk:biblioentry (dbk:biblioentry) = dbk:biblioentry *
- + dbk:bibliomixed (dbk:bibliomixed) = dbk:bibliomixed *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:biblioid] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
- - dbk:otherclass (String) 
-
-[dbk:bibliolist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:biblioentry (dbk:biblioentry) = dbk:biblioentry *
- + dbk:bibliomixed (dbk:bibliomixed) = dbk:bibliomixed *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:bibliomisc] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:bibliomixed] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:publishingInlines
- + dbk:abstract (dbk:abstract) = dbk:abstract *
- + dbk:address (dbk:address) = dbk:address *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums *
- + dbk:author (dbk:author) = dbk:author *
- + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup *
- + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials *
- + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage *
- + dbk:biblioid (dbk:biblioid) = dbk:biblioid *
- + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc *
- + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset *
- + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation *
- + dbk:biblioset (dbk:biblioset) = dbk:biblioset *
- + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource *
- + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid *
- + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry *
- + dbk:citetitle (dbk:citetitle) = dbk:citetitle *
- + dbk:collab (dbk:collab) = dbk:collab *
- + dbk:confgroup (dbk:confgroup) = dbk:confgroup *
- + dbk:contractnum (dbk:contractnum) = dbk:contractnum *
- + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor *
- + dbk:copyright (dbk:copyright) = dbk:copyright *
- + dbk:cover (dbk:cover) = dbk:cover *
- + dbk:edition (dbk:edition) = dbk:edition *
- + dbk:editor (dbk:editor) = dbk:editor *
- + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink *
- + dbk:issuenum (dbk:issuenum) = dbk:issuenum *
- + dbk:itermset (dbk:itermset) = dbk:itermset *
- + dbk:keywordset (dbk:keywordset) = dbk:keywordset *
- + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:org (dbk:org) = dbk:org *
- + dbk:orgname (dbk:orgname) = dbk:orgname *
- + dbk:othercredit (dbk:othercredit) = dbk:othercredit *
- + dbk:pagenums (dbk:pagenums) = dbk:pagenums *
- + dbk:person (dbk:person) = dbk:person *
- + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
- + dbk:personname (dbk:personname) = dbk:personname *
- + dbk:phrase (dbk:phrase) = dbk:phrase *
- + dbk:printhistory (dbk:printhistory) = dbk:printhistory *
- + dbk:productname (dbk:productname) = dbk:productname *
- + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
- + dbk:pubdate (dbk:pubdate) = dbk:pubdate *
- + dbk:publisher (dbk:publisher) = dbk:publisher *
- + dbk:publishername (dbk:publishername) = dbk:publishername *
- + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums *
- + dbk:subjectset (dbk:subjectset) = dbk:subjectset *
- + dbk:subscript (dbk:subscript) = dbk:subscript *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- + dbk:superscript (dbk:superscript) = dbk:superscript *
- + dbk:title (dbk:title) = dbk:title *
- + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev *
- + dbk:volumenum (dbk:volumenum) = dbk:volumenum *
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:bibliomset] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:publishingInlines, argeodbk:ubiquitousInlines
- + dbk:abstract (dbk:abstract) = dbk:abstract *
- + dbk:address (dbk:address) = dbk:address *
- + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums *
- + dbk:author (dbk:author) = dbk:author *
- + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup *
- + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials *
- + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage *
- + dbk:biblioid (dbk:biblioid) = dbk:biblioid *
- + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc *
- + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset *
- + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation *
- + dbk:biblioset (dbk:biblioset) = dbk:biblioset *
- + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource *
- + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid *
- + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry *
- + dbk:citetitle (dbk:citetitle) = dbk:citetitle *
- + dbk:collab (dbk:collab) = dbk:collab *
- + dbk:confgroup (dbk:confgroup) = dbk:confgroup *
- + dbk:contractnum (dbk:contractnum) = dbk:contractnum *
- + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor *
- + dbk:copyright (dbk:copyright) = dbk:copyright *
- + dbk:cover (dbk:cover) = dbk:cover *
- + dbk:edition (dbk:edition) = dbk:edition *
- + dbk:editor (dbk:editor) = dbk:editor *
- + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink *
- + dbk:issuenum (dbk:issuenum) = dbk:issuenum *
- + dbk:itermset (dbk:itermset) = dbk:itermset *
- + dbk:keywordset (dbk:keywordset) = dbk:keywordset *
- + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:org (dbk:org) = dbk:org *
- + dbk:orgname (dbk:orgname) = dbk:orgname *
- + dbk:othercredit (dbk:othercredit) = dbk:othercredit *
- + dbk:pagenums (dbk:pagenums) = dbk:pagenums *
- + dbk:person (dbk:person) = dbk:person *
- + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
- + dbk:personname (dbk:personname) = dbk:personname *
- + dbk:printhistory (dbk:printhistory) = dbk:printhistory *
- + dbk:productname (dbk:productname) = dbk:productname *
- + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
- + dbk:pubdate (dbk:pubdate) = dbk:pubdate *
- + dbk:publisher (dbk:publisher) = dbk:publisher *
- + dbk:publishername (dbk:publishername) = dbk:publishername *
- + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums *
- + dbk:subjectset (dbk:subjectset) = dbk:subjectset *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- + dbk:title (dbk:title) = dbk:title *
- + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev *
- + dbk:volumenum (dbk:volumenum) = dbk:volumenum *
- - dbk:relation (String) 
-
-[dbk:biblioref] > argeodbk:base, argeodbk:linkingAttributes
- - dbk:begin (String) 
- - dbk:end (String) 
- - dbk:endterm (Reference) 
- - dbk:units (String) 
- - dbk:xrefstyle (String) 
-
-[dbk:bibliorelation] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
- - dbk:otherclass (String) 
- - dbk:othertype (String) 
- - dbk:type (String) 
-
-[dbk:biblioset] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:publishingInlines
- + dbk:abstract (dbk:abstract) = dbk:abstract *
- + dbk:address (dbk:address) = dbk:address *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums *
- + dbk:author (dbk:author) = dbk:author *
- + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup *
- + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials *
- + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage *
- + dbk:biblioid (dbk:biblioid) = dbk:biblioid *
- + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc *
- + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset *
- + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation *
- + dbk:biblioset (dbk:biblioset) = dbk:biblioset *
- + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource *
- + dbk:citebiblioid (dbk:citebiblioid) = dbk:citebiblioid *
- + dbk:citerefentry (dbk:citerefentry) = dbk:citerefentry *
- + dbk:citetitle (dbk:citetitle) = dbk:citetitle *
- + dbk:collab (dbk:collab) = dbk:collab *
- + dbk:confgroup (dbk:confgroup) = dbk:confgroup *
- + dbk:contractnum (dbk:contractnum) = dbk:contractnum *
- + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor *
- + dbk:copyright (dbk:copyright) = dbk:copyright *
- + dbk:cover (dbk:cover) = dbk:cover *
- + dbk:edition (dbk:edition) = dbk:edition *
- + dbk:editor (dbk:editor) = dbk:editor *
- + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink *
- + dbk:issuenum (dbk:issuenum) = dbk:issuenum *
- + dbk:itermset (dbk:itermset) = dbk:itermset *
- + dbk:keywordset (dbk:keywordset) = dbk:keywordset *
- + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:org (dbk:org) = dbk:org *
- + dbk:orgname (dbk:orgname) = dbk:orgname *
- + dbk:othercredit (dbk:othercredit) = dbk:othercredit *
- + dbk:pagenums (dbk:pagenums) = dbk:pagenums *
- + dbk:person (dbk:person) = dbk:person *
- + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
- + dbk:personname (dbk:personname) = dbk:personname *
- + dbk:phrase (dbk:phrase) = dbk:phrase *
- + dbk:printhistory (dbk:printhistory) = dbk:printhistory *
- + dbk:productname (dbk:productname) = dbk:productname *
- + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
- + dbk:pubdate (dbk:pubdate) = dbk:pubdate *
- + dbk:publisher (dbk:publisher) = dbk:publisher *
- + dbk:publishername (dbk:publishername) = dbk:publishername *
- + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums *
- + dbk:subjectset (dbk:subjectset) = dbk:subjectset *
- + dbk:subscript (dbk:subscript) = dbk:subscript *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- + dbk:superscript (dbk:superscript) = dbk:superscript *
- + dbk:title (dbk:title) = dbk:title *
- + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev *
- + dbk:volumenum (dbk:volumenum) = dbk:volumenum *
- - dbk:relation (String) 
-
-[dbk:bibliosource] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
- - dbk:otherclass (String) 
-
-[dbk:blockquote] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:attribution (dbk:attribution) = dbk:attribution
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:book] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:acknowledgements (dbk:acknowledgements) = dbk:acknowledgements *
- + dbk:appendix (dbk:appendix) = dbk:appendix *
- + dbk:article (dbk:article) = dbk:article *
- + dbk:bibliography (dbk:bibliography) = dbk:bibliography *
- + dbk:chapter (dbk:chapter) = dbk:chapter *
- + dbk:colophon (dbk:colophon) = dbk:colophon *
- + dbk:dedication (dbk:dedication) = dbk:dedication *
- + dbk:glossary (dbk:glossary) = dbk:glossary *
- + dbk:index (dbk:index) = dbk:index *
- + dbk:part (dbk:part) = dbk:part *
- + dbk:preface (dbk:preface) = dbk:preface *
- + dbk:reference (dbk:reference) = dbk:reference *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- + dbk:toc (dbk:toc) = dbk:toc *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:bridgehead] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:otherrenderas (String) 
- - dbk:renderas (String) 
-
-[dbk:callout] > argeodbk:base, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:arearefs (String) 
-
-[dbk:calloutlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:callout (dbk:callout) = dbk:callout *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:caption] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
- - dbk:class (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:style (String) 
- - dbk:title (String) 
-
-[dbk:caution] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:chapter] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:refentry (dbk:refentry) = dbk:refentry *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:sect1 (dbk:sect1) = dbk:sect1 *
- + dbk:section (dbk:section) = dbk:section *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
-
-[dbk:citation] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:citebiblioid] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
- - dbk:otherclass (String) 
-
-[dbk:citerefentry] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:manvolnum (dbk:manvolnum) = dbk:manvolnum
- + dbk:refentrytitle (dbk:refentrytitle) = dbk:refentrytitle
-
-[dbk:citetitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:pubwork (String) 
-
-[dbk:city] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:classname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:classsynopsis] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:classsynopsisinfo (dbk:classsynopsisinfo) = dbk:classsynopsisinfo *
- + dbk:constructorsynopsis (dbk:constructorsynopsis) = dbk:constructorsynopsis *
- + dbk:destructorsynopsis (dbk:destructorsynopsis) = dbk:destructorsynopsis *
- + dbk:fieldsynopsis (dbk:fieldsynopsis) = dbk:fieldsynopsis *
- + dbk:methodsynopsis (dbk:methodsynopsis) = dbk:methodsynopsis *
- + dbk:ooclass (dbk:ooclass) = dbk:ooclass *
- + dbk:ooexception (dbk:ooexception) = dbk:ooexception *
- + dbk:oointerface (dbk:oointerface) = dbk:oointerface *
- - dbk:class (String) 
- - dbk:language (String) 
-
-[dbk:classsynopsisinfo] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- + dbk:co (dbk:co) = dbk:co *
- + dbk:info (dbk:info) = dbk:info *
- + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
- + dbk:textobject (dbk:textobject) = dbk:textobject *
- - dbk:continuation (String) 
- - dbk:language (String) 
- - dbk:linenumbering (String) 
- - dbk:startinglinenumber (String) 
- - xml:space (String) 
-
-[dbk:cmdsynopsis] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:arg (dbk:arg) = dbk:arg *
- + dbk:command (dbk:command) = dbk:command *
- + dbk:group (dbk:group) = dbk:group *
- + dbk:info (dbk:info) = dbk:info
- + dbk:sbr (dbk:sbr) = dbk:sbr *
- + dbk:synopfragment (dbk:synopfragment) = dbk:synopfragment *
- - dbk:cmdlength (String) 
- - dbk:label (String) 
- - dbk:sepchar (String) 
-
-[dbk:co] > argeodbk:base
- - dbk:label (String) 
- - dbk:linkends (String) 
-
-[dbk:code] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:classname (dbk:classname) = dbk:classname *
- + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname *
- + dbk:function (dbk:function) = dbk:function *
- + dbk:initializer (dbk:initializer) = dbk:initializer *
- + dbk:interfacename (dbk:interfacename) = dbk:interfacename *
- + dbk:methodname (dbk:methodname) = dbk:methodname *
- + dbk:modifier (dbk:modifier) = dbk:modifier *
- + dbk:ooclass (dbk:ooclass) = dbk:ooclass *
- + dbk:ooexception (dbk:ooexception) = dbk:ooexception *
- + dbk:oointerface (dbk:oointerface) = dbk:oointerface *
- + dbk:parameter (dbk:parameter) = dbk:parameter *
- + dbk:returnvalue (dbk:returnvalue) = dbk:returnvalue *
- + dbk:type (dbk:type) = dbk:type *
- + dbk:varname (dbk:varname) = dbk:varname *
- - dbk:language (String) 
-
-[dbk:col] > nt:base
- - dbk:align (String) 
- - dbk:annotations (String) 
- - dbk:arch (String) 
- - dbk:audience (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:class (String) 
- - dbk:condition (String) 
- - dbk:conformance (String) 
- - dbk:dir (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:os (String) 
- - dbk:remap (String) 
- - dbk:revision (String) 
- - dbk:revisionflag (String) 
- - dbk:security (String) 
- - dbk:span (String) 
- - dbk:style (String) 
- - dbk:title (String) 
- - dbk:userlevel (String) 
- - dbk:valign (String) 
- - dbk:vendor (String) 
- - dbk:version (String) 
- - dbk:width (String) 
- - dbk:wordsize (String) 
- - dbk:xreflabel (String) 
- - xml:base (String) 
- - xml:id (String) 
- - xml:lang (String) 
-
-[dbk:colgroup] > nt:base
- + dbk:col (dbk:col) = dbk:col *
- - dbk:align (String) 
- - dbk:annotations (String) 
- - dbk:arch (String) 
- - dbk:audience (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:class (String) 
- - dbk:condition (String) 
- - dbk:conformance (String) 
- - dbk:dir (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:os (String) 
- - dbk:remap (String) 
- - dbk:revision (String) 
- - dbk:revisionflag (String) 
- - dbk:security (String) 
- - dbk:span (String) 
- - dbk:style (String) 
- - dbk:title (String) 
- - dbk:userlevel (String) 
- - dbk:valign (String) 
- - dbk:vendor (String) 
- - dbk:version (String) 
- - dbk:width (String) 
- - dbk:wordsize (String) 
- - dbk:xreflabel (String) 
- - xml:base (String) 
- - xml:id (String) 
- - xml:lang (String) 
-
-[dbk:collab] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:affiliation (dbk:affiliation) = dbk:affiliation *
- + dbk:org (dbk:org) = dbk:org *
- + dbk:orgname (dbk:orgname) = dbk:orgname *
- + dbk:person (dbk:person) = dbk:person *
- + dbk:personname (dbk:personname) = dbk:personname *
-
-[dbk:colophon] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:colspec] > argeodbk:base, argeodbk:linkingAttributes
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:colname (String) 
- - dbk:colnum (String) 
- - dbk:colsep (String) 
- - dbk:colwidth (String) 
- - dbk:rowsep (String) 
-
-[dbk:command] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:computeroutput] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:ubiquitousInlines
- + dbk:co (dbk:co) = dbk:co *
- + dbk:command (dbk:command) = dbk:command *
- + dbk:computeroutput (dbk:computeroutput) = dbk:computeroutput *
- + dbk:envar (dbk:envar) = dbk:envar *
- + dbk:filename (dbk:filename) = dbk:filename *
- + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal *
- + dbk:option (dbk:option) = dbk:option *
- + dbk:optional (dbk:optional) = dbk:optional *
- + dbk:package (dbk:package) = dbk:package *
- + dbk:parameter (dbk:parameter) = dbk:parameter *
- + dbk:prompt (dbk:prompt) = dbk:prompt *
- + dbk:property (dbk:property) = dbk:property *
- + dbk:replaceable (dbk:replaceable) = dbk:replaceable *
- + dbk:systemitem (dbk:systemitem) = dbk:systemitem *
- + dbk:termdef (dbk:termdef) = dbk:termdef *
- + dbk:userinput (dbk:userinput) = dbk:userinput *
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:confdates] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:confgroup] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:address (dbk:address) = dbk:address *
- + dbk:confdates (dbk:confdates) = dbk:confdates *
- + dbk:confnum (dbk:confnum) = dbk:confnum *
- + dbk:confsponsor (dbk:confsponsor) = dbk:confsponsor *
- + dbk:conftitle (dbk:conftitle) = dbk:conftitle *
-
-[dbk:confnum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:confsponsor] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:conftitle] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:constant] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
-
-[dbk:constraint] > argeodbk:base, argeodbk:linkingAttributes
-
-[dbk:constraintdef] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:constructorsynopsis] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname *
- + dbk:methodname (dbk:methodname) = dbk:methodname
- + dbk:methodparam (dbk:methodparam) = dbk:methodparam *
- + dbk:modifier (dbk:modifier) = dbk:modifier *
- + dbk:void (dbk:void) = dbk:void
- - dbk:language (String) 
-
-[dbk:contractnum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:contractsponsor] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:contrib] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:copyright] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:holder (dbk:holder) = dbk:holder *
- + dbk:year (dbk:year) = dbk:year *
-
-[dbk:coref] > argeodbk:base, argeodbk:linkingAttributes
- - dbk:label (String) 
-
-[dbk:country] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:cover] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:classsynopsis (dbk:classsynopsis) = dbk:classsynopsis *
- + dbk:cmdsynopsis (dbk:cmdsynopsis) = dbk:cmdsynopsis *
- + dbk:constraintdef (dbk:constraintdef) = dbk:constraintdef *
- + dbk:constructorsynopsis (dbk:constructorsynopsis) = dbk:constructorsynopsis *
- + dbk:destructorsynopsis (dbk:destructorsynopsis) = dbk:destructorsynopsis *
- + dbk:fieldsynopsis (dbk:fieldsynopsis) = dbk:fieldsynopsis *
- + dbk:funcsynopsis (dbk:funcsynopsis) = dbk:funcsynopsis *
- + dbk:informalequation (dbk:informalequation) = dbk:informalequation *
- + dbk:informalexample (dbk:informalexample) = dbk:informalexample *
- + dbk:informalfigure (dbk:informalfigure) = dbk:informalfigure *
- + dbk:informaltable (dbk:informaltable) = dbk:informaltable *
- + dbk:literallayout (dbk:literallayout) = dbk:literallayout *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:methodsynopsis (dbk:methodsynopsis) = dbk:methodsynopsis *
- + dbk:msgset (dbk:msgset) = dbk:msgset *
- + dbk:productionset (dbk:productionset) = dbk:productionset *
- + dbk:programlisting (dbk:programlisting) = dbk:programlisting *
- + dbk:programlistingco (dbk:programlistingco) = dbk:programlistingco *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screen (dbk:screen) = dbk:screen *
- + dbk:screenco (dbk:screenco) = dbk:screenco *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:synopsis (dbk:synopsis) = dbk:synopsis *
- + dbk:task (dbk:task) = dbk:task *
-
-[dbk:database] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
-
-[dbk:date] > argeodbk:base, argeodbk:linkingAttributes
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:dedication] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:destructorsynopsis] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname *
- + dbk:methodname (dbk:methodname) = dbk:methodname
- + dbk:methodparam (dbk:methodparam) = dbk:methodparam *
- + dbk:modifier (dbk:modifier) = dbk:modifier *
- + dbk:void (dbk:void) = dbk:void
- - dbk:language (String) 
-
-[dbk:edition] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:editor] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:address (dbk:address) = dbk:address *
- + dbk:affiliation (dbk:affiliation) = dbk:affiliation *
- + dbk:contrib (dbk:contrib) = dbk:contrib *
- + dbk:email (dbk:email) = dbk:email *
- + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
- + dbk:orgname (dbk:orgname) = dbk:orgname
- + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
- + dbk:personname (dbk:personname) = dbk:personname
- + dbk:uri (dbk:uri) = dbk:uri *
-
-[dbk:email] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:emphasis] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:entry] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:colname (String) 
- - dbk:colsep (String) 
- - dbk:morerows (String) 
- - dbk:nameend (String) 
- - dbk:namest (String) 
- - dbk:rotate (String) 
- - dbk:rowsep (String) 
- - dbk:spanname (String) 
- - dbk:valign (String) 
-
-[dbk:entrytbl] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:colspec (dbk:colspec) = dbk:colspec *
- + dbk:spanspec (dbk:spanspec) = dbk:spanspec *
- + dbk:tbody (dbk:tbody) = dbk:tbody
- + dbk:thead (dbk:thead) = dbk:thead
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:colname (String) 
- - dbk:cols (String) 
- - dbk:colsep (String) 
- - dbk:nameend (String) 
- - dbk:namest (String) 
- - dbk:rowsep (String) 
- - dbk:spanname (String) 
- - dbk:tgroupstyle (String) 
-
-[dbk:envar] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:epigraph] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:attribution (dbk:attribution) = dbk:attribution
- + dbk:info (dbk:info) = dbk:info
- + dbk:literallayout (dbk:literallayout) = dbk:literallayout *
-
-[dbk:equation] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:alt (dbk:alt) = dbk:alt
- + dbk:caption (dbk:caption) = dbk:caption
- + dbk:mathphrase (dbk:mathphrase) = dbk:mathphrase *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- - dbk:floatstyle (String) 
- - dbk:label (String) 
- - dbk:pgwide (String) 
-
-[dbk:errorcode] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:errorname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:errortext] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:errortype] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:example] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:caption (dbk:caption) = dbk:caption
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:floatstyle (String) 
- - dbk:label (String) 
- - dbk:pgwide (String) 
- - dbk:width (String) 
-
-[dbk:exceptionname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:extendedlink] > argeodbk:base
- + dbk:arc (dbk:arc) = dbk:arc *
- + dbk:locator (dbk:locator) = dbk:locator *
-
-[dbk:fax] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:fieldsynopsis] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:initializer (dbk:initializer) = dbk:initializer
- + dbk:modifier (dbk:modifier) = dbk:modifier *
- + dbk:type (dbk:type) = dbk:type
- + dbk:varname (dbk:varname) = dbk:varname
- - dbk:language (String) 
-
-[dbk:figure] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:caption (dbk:caption) = dbk:caption
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:floatstyle (String) 
- - dbk:label (String) 
- - dbk:pgwide (String) 
-
-[dbk:filename] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
- - dbk:path (String) 
-
-[dbk:firstname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:firstterm] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:baseform (String) 
-
-[dbk:footnote] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:label (String) 
-
-[dbk:footnoteref] > argeodbk:base, argeodbk:linkingAttributes
- - dbk:label (String) 
-
-[dbk:foreignphrase] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:publishingInlines
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:application (dbk:application) = dbk:application *
- + dbk:biblioref (dbk:biblioref) = dbk:biblioref *
- + dbk:database (dbk:database) = dbk:database *
- + dbk:hardware (dbk:hardware) = dbk:hardware *
- + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
- + dbk:link (dbk:link) = dbk:link *
- + dbk:olink (dbk:olink) = dbk:olink *
- + dbk:phrase (dbk:phrase) = dbk:phrase *
- + dbk:productname (dbk:productname) = dbk:productname *
- + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
- + dbk:subscript (dbk:subscript) = dbk:subscript *
- + dbk:superscript (dbk:superscript) = dbk:superscript *
- + dbk:trademark (dbk:trademark) = dbk:trademark *
- + dbk:xref (dbk:xref) = dbk:xref *
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:formalpara] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:para (dbk:para) = dbk:para
-
-[dbk:funcdef] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:function (dbk:function) = dbk:function *
- + dbk:type (dbk:type) = dbk:type *
-
-[dbk:funcparams] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:funcprototype] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:funcdef (dbk:funcdef) = dbk:funcdef
- + dbk:modifier (dbk:modifier) = dbk:modifier *
- + dbk:paramdef (dbk:paramdef) = dbk:paramdef *
- + dbk:varargs (dbk:varargs) = dbk:varargs
- + dbk:varargs (dbk:varargs) = dbk:varargs
- + dbk:void (dbk:void) = dbk:void
-
-[dbk:funcsynopsis] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:funcprototype (dbk:funcprototype) = dbk:funcprototype *
- + dbk:funcsynopsisinfo (dbk:funcsynopsisinfo) = dbk:funcsynopsisinfo *
- + dbk:info (dbk:info) = dbk:info
- - dbk:language (String) 
-
-[dbk:funcsynopsisinfo] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- + dbk:co (dbk:co) = dbk:co *
- + dbk:info (dbk:info) = dbk:info *
- + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
- + dbk:textobject (dbk:textobject) = dbk:textobject *
- - dbk:continuation (String) 
- - dbk:language (String) 
- - dbk:linenumbering (String) 
- - dbk:startinglinenumber (String) 
- - xml:space (String) 
-
-[dbk:function] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:glossary] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bibliography (dbk:bibliography) = dbk:bibliography
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:glossdiv (dbk:glossdiv) = dbk:glossdiv *
- + dbk:glossentry (dbk:glossentry) = dbk:glossentry *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:glossdef] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:glossseealso (dbk:glossseealso) = dbk:glossseealso *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:subject (String) 
-
-[dbk:glossdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:glossentry (dbk:glossentry) = dbk:glossentry *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:glossentry] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes
- + dbk:abbrev (dbk:abbrev) = dbk:abbrev
- + dbk:acronym (dbk:acronym) = dbk:acronym
- + dbk:glossdef (dbk:glossdef) = dbk:glossdef *
- + dbk:glosssee (dbk:glosssee) = dbk:glosssee
- + dbk:glossterm (dbk:glossterm) = dbk:glossterm
- - dbk:sortas (String) 
-
-[dbk:glosslist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:glossentry (dbk:glossentry) = dbk:glossentry *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:glosssee] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:otherterm (Reference) 
-
-[dbk:glossseealso] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:otherterm (Reference) 
-
-[dbk:glossterm] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:baseform (String) 
-
-[dbk:group] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:arg (dbk:arg) = dbk:arg *
- + dbk:group (dbk:group) = dbk:group *
- + dbk:option (dbk:option) = dbk:option *
- + dbk:replaceable (dbk:replaceable) = dbk:replaceable *
- + dbk:sbr (dbk:sbr) = dbk:sbr *
- + dbk:synopfragmentref (dbk:synopfragmentref) = dbk:synopfragmentref *
- - dbk:choice (String) 
- - dbk:rep (String) 
-
-[dbk:guibutton] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:accel (dbk:accel) = dbk:accel *
-
-[dbk:guiicon] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:accel (dbk:accel) = dbk:accel *
-
-[dbk:guilabel] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:accel (dbk:accel) = dbk:accel *
-
-[dbk:guimenu] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:accel (dbk:accel) = dbk:accel *
-
-[dbk:guimenuitem] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:accel (dbk:accel) = dbk:accel *
-
-[dbk:guisubmenu] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:accel (dbk:accel) = dbk:accel *
-
-[dbk:hardware] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:holder] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:honorific] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:imagedata] > argeodbk:base
- + dbk:info (dbk:info) = dbk:info
- - dbk:align (String) 
- - dbk:contentdepth (String) 
- - dbk:contentwidth (String) 
- - dbk:depth (String) 
- - dbk:entityref (String) 
- - dbk:fileref (String) 
- - dbk:format (String) 
- - dbk:scale (String) 
- - dbk:scalefit (String) 
- - dbk:valign (String) 
- - dbk:width (String) 
-
-[dbk:imageobject] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:imagedata (dbk:imagedata) = dbk:imagedata
- + dbk:info (dbk:info) = dbk:info
-
-[dbk:imageobjectco] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:areaspec (dbk:areaspec) = dbk:areaspec
- + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist *
- + dbk:imageobject (dbk:imageobject) = dbk:imageobject *
- + dbk:info (dbk:info) = dbk:info
-
-[dbk:important] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:index] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:indexdiv (dbk:indexdiv) = dbk:indexdiv *
- + dbk:indexentry (dbk:indexentry) = dbk:indexentry *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
- - dbk:type (String) 
-
-[dbk:indexdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:indexentry (dbk:indexentry) = dbk:indexentry *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:indexentry] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:primaryie (dbk:primaryie) = dbk:primaryie
- + dbk:secondaryie (dbk:secondaryie) = dbk:secondaryie *
- + dbk:seealsoie (dbk:seealsoie) = dbk:seealsoie *
- + dbk:seeie (dbk:seeie) = dbk:seeie *
- + dbk:tertiaryie (dbk:tertiaryie) = dbk:tertiaryie *
-
-[dbk:indexterm] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:primary (dbk:primary) = dbk:primary
- + dbk:secondary (dbk:secondary) = dbk:secondary
- + dbk:see (dbk:see) = dbk:see
- + dbk:seealso (dbk:seealso) = dbk:seealso *
- + dbk:tertiary (dbk:tertiary) = dbk:tertiary
- - dbk:class (String) 
- - dbk:pagenum (String) 
- - dbk:scope (String) 
- - dbk:significance (String) 
- - dbk:startref (Reference) 
- - dbk:type (String) 
- - dbk:zone (String) 
-
-[dbk:info] > argeodbk:base
- + dbk:abstract (dbk:abstract) = dbk:abstract *
- + dbk:address (dbk:address) = dbk:address *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:artpagenums (dbk:artpagenums) = dbk:artpagenums *
- + dbk:author (dbk:author) = dbk:author *
- + dbk:authorgroup (dbk:authorgroup) = dbk:authorgroup *
- + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials *
- + dbk:bibliocoverage (dbk:bibliocoverage) = dbk:bibliocoverage *
- + dbk:biblioid (dbk:biblioid) = dbk:biblioid *
- + dbk:bibliomisc (dbk:bibliomisc) = dbk:bibliomisc *
- + dbk:bibliomset (dbk:bibliomset) = dbk:bibliomset *
- + dbk:bibliorelation (dbk:bibliorelation) = dbk:bibliorelation *
- + dbk:biblioset (dbk:biblioset) = dbk:biblioset *
- + dbk:bibliosource (dbk:bibliosource) = dbk:bibliosource *
- + dbk:collab (dbk:collab) = dbk:collab *
- + dbk:confgroup (dbk:confgroup) = dbk:confgroup *
- + dbk:contractnum (dbk:contractnum) = dbk:contractnum *
- + dbk:contractsponsor (dbk:contractsponsor) = dbk:contractsponsor *
- + dbk:copyright (dbk:copyright) = dbk:copyright *
- + dbk:cover (dbk:cover) = dbk:cover *
- + dbk:date (dbk:date) = dbk:date *
- + dbk:edition (dbk:edition) = dbk:edition *
- + dbk:editor (dbk:editor) = dbk:editor *
- + dbk:extendedlink (dbk:extendedlink) = dbk:extendedlink *
- + dbk:issuenum (dbk:issuenum) = dbk:issuenum *
- + dbk:itermset (dbk:itermset) = dbk:itermset *
- + dbk:keywordset (dbk:keywordset) = dbk:keywordset *
- + dbk:legalnotice (dbk:legalnotice) = dbk:legalnotice *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:org (dbk:org) = dbk:org *
- + dbk:orgname (dbk:orgname) = dbk:orgname *
- + dbk:othercredit (dbk:othercredit) = dbk:othercredit *
- + dbk:pagenums (dbk:pagenums) = dbk:pagenums *
- + dbk:printhistory (dbk:printhistory) = dbk:printhistory *
- + dbk:productname (dbk:productname) = dbk:productname *
- + dbk:productnumber (dbk:productnumber) = dbk:productnumber *
- + dbk:pubdate (dbk:pubdate) = dbk:pubdate *
- + dbk:publisher (dbk:publisher) = dbk:publisher *
- + dbk:publishername (dbk:publishername) = dbk:publishername *
- + dbk:releaseinfo (dbk:releaseinfo) = dbk:releaseinfo *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:seriesvolnums (dbk:seriesvolnums) = dbk:seriesvolnums *
- + dbk:subjectset (dbk:subjectset) = dbk:subjectset *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- + dbk:title (dbk:title) = dbk:title *
- + dbk:titleabbrev (dbk:titleabbrev) = dbk:titleabbrev *
- + dbk:volumenum (dbk:volumenum) = dbk:volumenum *
-
-[dbk:informalequation] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:alt (dbk:alt) = dbk:alt
- + dbk:caption (dbk:caption) = dbk:caption
- + dbk:info (dbk:info) = dbk:info
- + dbk:mathphrase (dbk:mathphrase) = dbk:mathphrase *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
-
-[dbk:informalexample] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:caption (dbk:caption) = dbk:caption
- + dbk:info (dbk:info) = dbk:info
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:floatstyle (String) 
- - dbk:width (String) 
-
-[dbk:informalfigure] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:caption (dbk:caption) = dbk:caption
- + dbk:info (dbk:info) = dbk:info
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:floatstyle (String) 
- - dbk:label (String) 
- - dbk:pgwide (String) 
-
-[dbk:informaltable] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:col (dbk:col) = dbk:col *
- + dbk:colgroup (dbk:colgroup) = dbk:colgroup *
- + dbk:info (dbk:info) = dbk:info
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:tbody (dbk:tbody) = dbk:tbody *
- + dbk:textobject (dbk:textobject) = dbk:textobject *
- + dbk:tfoot (dbk:tfoot) = dbk:tfoot
- + dbk:tgroup (dbk:tgroup) = dbk:tgroup *
- + dbk:thead (dbk:thead) = dbk:thead
- + dbk:tr (dbk:tr) = dbk:tr *
- - dbk:border (String) 
- - dbk:cellpadding (String) 
- - dbk:cellspacing (String) 
- - dbk:class (String) 
- - dbk:colsep (String) 
- - dbk:floatstyle (String) 
- - dbk:frame (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:orient (String) 
- - dbk:pgwide (String) 
- - dbk:rowheader (String) 
- - dbk:rowsep (String) 
- - dbk:rules (String) 
- - dbk:style (String) 
- - dbk:summary (String) 
- - dbk:tabstyle (String) 
- - dbk:title (String) 
- - dbk:width (String) 
-
-[dbk:initializer] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:inlineequation] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:alt (dbk:alt) = dbk:alt
- + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
- + dbk:mathphrase (dbk:mathphrase) = dbk:mathphrase *
-
-[dbk:inlinemediaobject] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:alt (dbk:alt) = dbk:alt
- + dbk:audioobject (dbk:audioobject) = dbk:audioobject *
- + dbk:imageobject (dbk:imageobject) = dbk:imageobject *
- + dbk:imageobjectco (dbk:imageobjectco) = dbk:imageobjectco *
- + dbk:info (dbk:info) = dbk:info
- + dbk:textobject (dbk:textobject) = dbk:textobject *
- + dbk:videoobject (dbk:videoobject) = dbk:videoobject *
-
-[dbk:interfacename] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:issuenum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:itemizedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:listitem (dbk:listitem) = dbk:listitem *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:mark (String) 
- - dbk:spacing (String) 
-
-[dbk:itermset] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes
-
-[dbk:jobtitle] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:keycap] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:function (String) 
- - dbk:otherfunction (String) 
-
-[dbk:keycode] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:keycombo] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:keycap (dbk:keycap) = dbk:keycap *
- + dbk:keycombo (dbk:keycombo) = dbk:keycombo *
- + dbk:keysym (dbk:keysym) = dbk:keysym *
- + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton *
- - dbk:action (String) 
- - dbk:otheraction (String) 
-
-[dbk:keysym] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:keyword] > argeodbk:base, argeodbk:linkingAttributes
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:keywordset] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:keyword (dbk:keyword) = dbk:keyword *
-
-[dbk:label] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:legalnotice] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:lhs] > argeodbk:base, argeodbk:linkingAttributes
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:lineage] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:lineannotation] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:link] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:endterm (Reference) 
- - dbk:xrefstyle (String) 
-
-[dbk:listitem] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:override (String) 
-
-[dbk:literal] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:literallayout] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- + dbk:co (dbk:co) = dbk:co *
- + dbk:info (dbk:info) = dbk:info *
- + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
- + dbk:textobject (dbk:textobject) = dbk:textobject *
- - dbk:class (String) 
- - dbk:continuation (String) 
- - dbk:language (String) 
- - dbk:linenumbering (String) 
- - dbk:startinglinenumber (String) 
- - xml:space (String) 
-
-[dbk:locator] > argeodbk:base
- - xlink:label (String) 
-
-[dbk:manvolnum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:markup] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:mathphrase] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:emphasis (dbk:emphasis) = dbk:emphasis *
-
-[dbk:mediaobject] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:alt (dbk:alt) = dbk:alt
- + dbk:audioobject (dbk:audioobject) = dbk:audioobject *
- + dbk:caption (dbk:caption) = dbk:caption
- + dbk:imageobject (dbk:imageobject) = dbk:imageobject *
- + dbk:imageobjectco (dbk:imageobjectco) = dbk:imageobjectco *
- + dbk:info (dbk:info) = dbk:info
- + dbk:textobject (dbk:textobject) = dbk:textobject *
- + dbk:videoobject (dbk:videoobject) = dbk:videoobject *
-
-[dbk:member] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:menuchoice] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:guibutton (dbk:guibutton) = dbk:guibutton *
- + dbk:guiicon (dbk:guiicon) = dbk:guiicon *
- + dbk:guilabel (dbk:guilabel) = dbk:guilabel *
- + dbk:guimenu (dbk:guimenu) = dbk:guimenu *
- + dbk:guimenuitem (dbk:guimenuitem) = dbk:guimenuitem *
- + dbk:guisubmenu (dbk:guisubmenu) = dbk:guisubmenu *
- + dbk:shortcut (dbk:shortcut) = dbk:shortcut
-
-[dbk:methodname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:methodparam] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:funcparams (dbk:funcparams) = dbk:funcparams
- + dbk:initializer (dbk:initializer) = dbk:initializer
- + dbk:modifier (dbk:modifier) = dbk:modifier *
- + dbk:modifier (dbk:modifier) = dbk:modifier *
- + dbk:parameter (dbk:parameter) = dbk:parameter
- + dbk:type (dbk:type) = dbk:type *
- - dbk:choice (String) 
- - dbk:rep (String) 
-
-[dbk:methodsynopsis] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname *
- + dbk:methodname (dbk:methodname) = dbk:methodname
- + dbk:methodparam (dbk:methodparam) = dbk:methodparam *
- + dbk:modifier (dbk:modifier) = dbk:modifier *
- + dbk:type (dbk:type) = dbk:type
- + dbk:void (dbk:void) = dbk:void
- - dbk:language (String) 
-
-[dbk:modifier] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - xml:space (String) 
-
-[dbk:mousebutton] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:msg] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:msgmain (dbk:msgmain) = dbk:msgmain
- + dbk:msgrel (dbk:msgrel) = dbk:msgrel *
- + dbk:msgsub (dbk:msgsub) = dbk:msgsub *
-
-[dbk:msgaud] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:msgentry] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:msg (dbk:msg) = dbk:msg *
- + dbk:msgexplan (dbk:msgexplan) = dbk:msgexplan *
- + dbk:msginfo (dbk:msginfo) = dbk:msginfo
-
-[dbk:msgexplan] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:msginfo] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:msgaud (dbk:msgaud) = dbk:msgaud *
- + dbk:msglevel (dbk:msglevel) = dbk:msglevel *
- + dbk:msgorig (dbk:msgorig) = dbk:msgorig *
-
-[dbk:msglevel] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:msgmain] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:msgtext (dbk:msgtext) = dbk:msgtext
-
-[dbk:msgorig] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:msgrel] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:msgtext (dbk:msgtext) = dbk:msgtext
-
-[dbk:msgset] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:msgentry (dbk:msgentry) = dbk:msgentry *
- + dbk:simplemsgentry (dbk:simplemsgentry) = dbk:simplemsgentry *
-
-[dbk:msgsub] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:msgtext (dbk:msgtext) = dbk:msgtext
-
-[dbk:msgtext] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:nonterminal] > argeodbk:base, argeodbk:linkingAttributes
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
- - dbk:def (String) 
-
-[dbk:note] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:olink] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:localinfo (String) 
- - dbk:targetdoc (String) 
- - dbk:targetptr (String) 
- - dbk:type (String) 
- - dbk:xrefstyle (String) 
-
-[dbk:ooclass] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:classname (dbk:classname) = dbk:classname
- + dbk:modifier (dbk:modifier) = dbk:modifier *
- + dbk:package (dbk:package) = dbk:package *
-
-[dbk:ooexception] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:exceptionname (dbk:exceptionname) = dbk:exceptionname
- + dbk:modifier (dbk:modifier) = dbk:modifier *
- + dbk:package (dbk:package) = dbk:package *
-
-[dbk:oointerface] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:interfacename (dbk:interfacename) = dbk:interfacename
- + dbk:modifier (dbk:modifier) = dbk:modifier *
- + dbk:package (dbk:package) = dbk:package *
-
-[dbk:option] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:optional] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:orderedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:listitem (dbk:listitem) = dbk:listitem *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:continuation (String) 
- - dbk:inheritnum (String) 
- - dbk:numeration (String) 
- - dbk:spacing (String) 
- - dbk:startingnumber (String) 
-
-[dbk:org] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:address (dbk:address) = dbk:address *
- + dbk:affiliation (dbk:affiliation) = dbk:affiliation *
- + dbk:email (dbk:email) = dbk:email *
- + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
- + dbk:orgname (dbk:orgname) = dbk:orgname
- + dbk:uri (dbk:uri) = dbk:uri *
-
-[dbk:orgdiv] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:orgname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
- - dbk:otherclass (String) 
-
-[dbk:otheraddr] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:othercredit] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:address (dbk:address) = dbk:address *
- + dbk:affiliation (dbk:affiliation) = dbk:affiliation *
- + dbk:contrib (dbk:contrib) = dbk:contrib *
- + dbk:email (dbk:email) = dbk:email *
- + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
- + dbk:orgname (dbk:orgname) = dbk:orgname
- + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
- + dbk:personname (dbk:personname) = dbk:personname
- + dbk:uri (dbk:uri) = dbk:uri *
- - dbk:class (String) 
- - dbk:otherclass (String) 
-
-[dbk:othername] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:package] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:pagenums] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:para] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:info (dbk:info) = dbk:info *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:paramdef] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:funcparams (dbk:funcparams) = dbk:funcparams *
- + dbk:initializer (dbk:initializer) = dbk:initializer *
- + dbk:parameter (dbk:parameter) = dbk:parameter *
- + dbk:type (dbk:type) = dbk:type *
- - dbk:choice (String) 
-
-[dbk:parameter] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
-
-[dbk:part] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:acknowledgements (dbk:acknowledgements) = dbk:acknowledgements *
- + dbk:appendix (dbk:appendix) = dbk:appendix *
- + dbk:article (dbk:article) = dbk:article *
- + dbk:bibliography (dbk:bibliography) = dbk:bibliography *
- + dbk:chapter (dbk:chapter) = dbk:chapter *
- + dbk:colophon (dbk:colophon) = dbk:colophon *
- + dbk:dedication (dbk:dedication) = dbk:dedication *
- + dbk:glossary (dbk:glossary) = dbk:glossary *
- + dbk:index (dbk:index) = dbk:index *
- + dbk:partintro (dbk:partintro) = dbk:partintro
- + dbk:preface (dbk:preface) = dbk:preface *
- + dbk:refentry (dbk:refentry) = dbk:refentry *
- + dbk:reference (dbk:reference) = dbk:reference *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- + dbk:toc (dbk:toc) = dbk:toc *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:partintro] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:refentry (dbk:refentry) = dbk:refentry *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:sect1 (dbk:sect1) = dbk:sect1 *
- + dbk:section (dbk:section) = dbk:section *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:person] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:address (dbk:address) = dbk:address *
- + dbk:affiliation (dbk:affiliation) = dbk:affiliation *
- + dbk:email (dbk:email) = dbk:email *
- + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
- + dbk:personname (dbk:personname) = dbk:personname
- + dbk:uri (dbk:uri) = dbk:uri *
-
-[dbk:personblurb] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
-
-[dbk:personname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:firstname (dbk:firstname) = dbk:firstname *
- + dbk:honorific (dbk:honorific) = dbk:honorific *
- + dbk:lineage (dbk:lineage) = dbk:lineage *
- + dbk:othername (dbk:othername) = dbk:othername *
- + dbk:surname (dbk:surname) = dbk:surname *
-
-[dbk:phone] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:phrase] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:pob] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:postcode] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:preface] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:refentry (dbk:refentry) = dbk:refentry *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:sect1 (dbk:sect1) = dbk:sect1 *
- + dbk:section (dbk:section) = dbk:section *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
-
-[dbk:primary] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:sortas (String) 
-
-[dbk:primaryie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:linkends (String) 
-
-[dbk:printhistory] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
-
-[dbk:procedure] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:step (dbk:step) = dbk:step *
-
-[dbk:production] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:constraint (dbk:constraint) = dbk:constraint *
- + dbk:lhs (dbk:lhs) = dbk:lhs
- + dbk:rhs (dbk:rhs) = dbk:rhs
-
-[dbk:productionrecap] > argeodbk:base, argeodbk:linkingAttributes
-
-[dbk:productionset] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:production (dbk:production) = dbk:production *
- + dbk:productionrecap (dbk:productionrecap) = dbk:productionrecap *
-
-[dbk:productname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
-
-[dbk:productnumber] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:programlisting] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- + dbk:co (dbk:co) = dbk:co *
- + dbk:info (dbk:info) = dbk:info *
- + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
- + dbk:textobject (dbk:textobject) = dbk:textobject *
- - dbk:continuation (String) 
- - dbk:language (String) 
- - dbk:linenumbering (String) 
- - dbk:startinglinenumber (String) 
- - dbk:width (String) 
- - xml:space (String) 
-
-[dbk:programlistingco] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:areaspec (dbk:areaspec) = dbk:areaspec
- + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist *
- + dbk:info (dbk:info) = dbk:info
- + dbk:programlisting (dbk:programlisting) = dbk:programlisting
-
-[dbk:prompt] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:co (dbk:co) = dbk:co *
-
-[dbk:property] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:pubdate] > argeodbk:base, argeodbk:linkingAttributes
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:publisher] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:address (dbk:address) = dbk:address *
- + dbk:publishername (dbk:publishername) = dbk:publishername
-
-[dbk:publishername] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:qandadiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:qandadiv (dbk:qandadiv) = dbk:qandadiv *
- + dbk:qandaentry (dbk:qandaentry) = dbk:qandaentry *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:qandaentry] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:answer (dbk:answer) = dbk:answer *
- + dbk:question (dbk:question) = dbk:question
-
-[dbk:qandaset] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:qandadiv (dbk:qandadiv) = dbk:qandadiv *
- + dbk:qandaentry (dbk:qandaentry) = dbk:qandaentry *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:defaultlabel (String) 
-
-[dbk:question] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:label (dbk:label) = dbk:label
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:quote] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:refclass] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:application (dbk:application) = dbk:application *
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:refdescriptor] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:refentry] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes
- + dbk:info (dbk:info) = dbk:info
- + dbk:refmeta (dbk:refmeta) = dbk:refmeta
- + dbk:refnamediv (dbk:refnamediv) = dbk:refnamediv *
- + dbk:refsect1 (dbk:refsect1) = dbk:refsect1 *
- + dbk:refsection (dbk:refsection) = dbk:refsection *
- + dbk:refsynopsisdiv (dbk:refsynopsisdiv) = dbk:refsynopsisdiv
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:refentrytitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:reference] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:partintro (dbk:partintro) = dbk:partintro
- + dbk:refentry (dbk:refentry) = dbk:refentry *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:refmeta] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes
- + dbk:manvolnum (dbk:manvolnum) = dbk:manvolnum
- + dbk:refentrytitle (dbk:refentrytitle) = dbk:refentrytitle
- + dbk:refmiscinfo (dbk:refmiscinfo) = dbk:refmiscinfo *
-
-[dbk:refmiscinfo] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
- - dbk:otherclass (String) 
-
-[dbk:refname] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:refnamediv] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:refclass (dbk:refclass) = dbk:refclass *
- + dbk:refdescriptor (dbk:refdescriptor) = dbk:refdescriptor
- + dbk:refname (dbk:refname) = dbk:refname *
- + dbk:refpurpose (dbk:refpurpose) = dbk:refpurpose
-
-[dbk:refpurpose] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:refsect1] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:refsect2 (dbk:refsect2) = dbk:refsect2 *
- + dbk:refsect2 (dbk:refsect2) = dbk:refsect2 *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:refsect2] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:refsect3 (dbk:refsect3) = dbk:refsect3 *
- + dbk:refsect3 (dbk:refsect3) = dbk:refsect3 *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:refsect3] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:refsection] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:refsection (dbk:refsection) = dbk:refsection *
- + dbk:refsection (dbk:refsection) = dbk:refsection *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:refsynopsisdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:refsect2 (dbk:refsect2) = dbk:refsect2 *
- + dbk:refsection (dbk:refsection) = dbk:refsection *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
-
-[dbk:releaseinfo] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:remark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:replaceable] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:co (dbk:co) = dbk:co *
- - dbk:class (String) 
-
-[dbk:returnvalue] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:revdescription] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:revhistory] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:revision (dbk:revision) = dbk:revision *
-
-[dbk:revision] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:author (dbk:author) = dbk:author *
- + dbk:authorinitials (dbk:authorinitials) = dbk:authorinitials *
- + dbk:date (dbk:date) = dbk:date
- + dbk:revdescription (dbk:revdescription) = dbk:revdescription
- + dbk:revnumber (dbk:revnumber) = dbk:revnumber
- + dbk:revremark (dbk:revremark) = dbk:revremark
-
-[dbk:revnumber] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:revremark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:rhs] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
- + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal *
- + dbk:sbr (dbk:sbr) = dbk:sbr *
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:row] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:entry (dbk:entry) = dbk:entry *
- + dbk:entrytbl (dbk:entrytbl) = dbk:entrytbl *
- - dbk:rowsep (String) 
- - dbk:valign (String) 
-
-[dbk:sbr] > argeodbk:base
-
-[dbk:screen] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- + dbk:co (dbk:co) = dbk:co *
- + dbk:info (dbk:info) = dbk:info *
- + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
- + dbk:textobject (dbk:textobject) = dbk:textobject *
- - dbk:continuation (String) 
- - dbk:language (String) 
- - dbk:linenumbering (String) 
- - dbk:startinglinenumber (String) 
- - dbk:width (String) 
- - xml:space (String) 
-
-[dbk:screenco] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:areaspec (dbk:areaspec) = dbk:areaspec
- + dbk:calloutlist (dbk:calloutlist) = dbk:calloutlist *
- + dbk:info (dbk:info) = dbk:info
- + dbk:screen (dbk:screen) = dbk:screen
-
-[dbk:screenshot] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
-
-[dbk:secondary] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:sortas (String) 
-
-[dbk:secondaryie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:linkends (String) 
-
-[dbk:sect1] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:sect2 (dbk:sect2) = dbk:sect2 *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
-
-[dbk:sect2] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:sect3 (dbk:sect3) = dbk:sect3 *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
-
-[dbk:sect3] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:sect4 (dbk:sect4) = dbk:sect4 *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
-
-[dbk:sect4] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:sect5 (dbk:sect5) = dbk:sect5 *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
-
-[dbk:sect5] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
-
-[dbk:section] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:refentry (dbk:refentry) = dbk:refentry *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:section (dbk:section) = dbk:section *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
- + dbk:simplesect (dbk:simplesect) = dbk:simplesect *
-
-[dbk:see] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:seealso] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:seealsoie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:linkends (String) 
-
-[dbk:seeie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:seg] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:seglistitem] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:seg (dbk:seg) = dbk:seg *
-
-[dbk:segmentedlist] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:seglistitem (dbk:seglistitem) = dbk:seglistitem *
- + dbk:segtitle (dbk:segtitle) = dbk:segtitle *
-
-[dbk:segtitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:seriesvolnums] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:set] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:book (dbk:book) = dbk:book *
- + dbk:set (dbk:set) = dbk:set *
- + dbk:setindex (dbk:setindex) = dbk:setindex
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- + dbk:toc (dbk:toc) = dbk:toc
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:setindex] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:indexdiv (dbk:indexdiv) = dbk:indexdiv *
- + dbk:indexentry (dbk:indexentry) = dbk:indexentry *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
- - dbk:type (String) 
-
-[dbk:shortaffil] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:shortcut] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:keycap (dbk:keycap) = dbk:keycap *
- + dbk:keycombo (dbk:keycombo) = dbk:keycombo *
- + dbk:keysym (dbk:keysym) = dbk:keysym *
- + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton *
- - dbk:action (String) 
- - dbk:otheraction (String) 
-
-[dbk:sidebar] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:simpara] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- + dbk:info (dbk:info) = dbk:info *
-
-[dbk:simplelist] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:member (dbk:member) = dbk:member *
- - dbk:columns (String) 
- - dbk:type (String) 
-
-[dbk:simplemsgentry] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:msgexplan (dbk:msgexplan) = dbk:msgexplan *
- + dbk:msgtext (dbk:msgtext) = dbk:msgtext
- - dbk:msgaud (String) 
- - dbk:msglevel (String) 
- - dbk:msgorig (String) 
-
-[dbk:simplesect] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:spanspec] > argeodbk:base, argeodbk:linkingAttributes
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:colsep (String) 
- - dbk:nameend (String) 
- - dbk:namest (String) 
- - dbk:rowsep (String) 
- - dbk:spanname (String) 
-
-[dbk:state] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:step] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:stepalternatives (dbk:stepalternatives) = dbk:stepalternatives
- + dbk:substeps (dbk:substeps) = dbk:substeps
- - dbk:performance (String) 
-
-[dbk:stepalternatives] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:info (dbk:info) = dbk:info
- + dbk:step (dbk:step) = dbk:step *
- - dbk:performance (String) 
-
-[dbk:street] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:subject] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:subjectterm (dbk:subjectterm) = dbk:subjectterm *
- - dbk:weight (String) 
-
-[dbk:subjectset] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:subject (dbk:subject) = dbk:subject *
- - dbk:scheme (String) 
-
-[dbk:subjectterm] > argeodbk:base, argeodbk:linkingAttributes
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:subscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:substeps] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:step (dbk:step) = dbk:step *
- - dbk:performance (String) 
-
-[dbk:subtitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:superscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:surname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:symbol] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
-
-[dbk:synopfragment] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:arg (dbk:arg) = dbk:arg *
- + dbk:group (dbk:group) = dbk:group *
-
-[dbk:synopfragmentref] > argeodbk:base, argeodbk:linkingAttributes
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:synopsis] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- + dbk:co (dbk:co) = dbk:co *
- + dbk:info (dbk:info) = dbk:info *
- + dbk:lineannotation (dbk:lineannotation) = dbk:lineannotation *
- + dbk:textobject (dbk:textobject) = dbk:textobject *
- - dbk:continuation (String) 
- - dbk:label (String) 
- - dbk:language (String) 
- - dbk:linenumbering (String) 
- - dbk:startinglinenumber (String) 
- - xml:space (String) 
-
-[dbk:systemitem] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- + dbk:co (dbk:co) = dbk:co *
- - dbk:class (String) 
-
-[dbk:table] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:caption (dbk:caption) = dbk:caption
- + dbk:col (dbk:col) = dbk:col *
- + dbk:colgroup (dbk:colgroup) = dbk:colgroup *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:tbody (dbk:tbody) = dbk:tbody *
- + dbk:textobject (dbk:textobject) = dbk:textobject *
- + dbk:tfoot (dbk:tfoot) = dbk:tfoot
- + dbk:tgroup (dbk:tgroup) = dbk:tgroup *
- + dbk:thead (dbk:thead) = dbk:thead
- + dbk:tr (dbk:tr) = dbk:tr *
- - dbk:border (String) 
- - dbk:cellpadding (String) 
- - dbk:cellspacing (String) 
- - dbk:class (String) 
- - dbk:colsep (String) 
- - dbk:floatstyle (String) 
- - dbk:frame (String) 
- - dbk:label (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:orient (String) 
- - dbk:pgwide (String) 
- - dbk:rowheader (String) 
- - dbk:rowsep (String) 
- - dbk:rules (String) 
- - dbk:shortentry (String) 
- - dbk:style (String) 
- - dbk:summary (String) 
- - dbk:tabstyle (String) 
- - dbk:title (String) 
- - dbk:tocentry (String) 
- - dbk:width (String) 
-
-[dbk:tag] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
- - dbk:namespace (String) 
-
-[dbk:task] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:example (dbk:example) = dbk:example *
- + dbk:procedure (dbk:procedure) = dbk:procedure
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- + dbk:taskprerequisites (dbk:taskprerequisites) = dbk:taskprerequisites
- + dbk:taskrelated (dbk:taskrelated) = dbk:taskrelated
- + dbk:tasksummary (dbk:tasksummary) = dbk:tasksummary
-
-[dbk:taskprerequisites] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:taskrelated] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:tasksummary] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:tbody] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:row (dbk:row) = dbk:row *
- + dbk:tr (dbk:tr) = dbk:tr *
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:class (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:style (String) 
- - dbk:title (String) 
- - dbk:valign (String) 
-
-[dbk:td] > argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:abbr (String) 
- - dbk:align (String) 
- - dbk:annotations (String) 
- - dbk:arch (String) 
- - dbk:audience (String) 
- - dbk:axis (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:class (String) 
- - dbk:colspan (String) 
- - dbk:condition (String) 
- - dbk:conformance (String) 
- - dbk:dir (String) 
- - dbk:headers (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:os (String) 
- - dbk:remap (String) 
- - dbk:revision (String) 
- - dbk:revisionflag (String) 
- - dbk:rowspan (String) 
- - dbk:scope (String) 
- - dbk:security (String) 
- - dbk:style (String) 
- - dbk:title (String) 
- - dbk:userlevel (String) 
- - dbk:valign (String) 
- - dbk:vendor (String) 
- - dbk:version (String) 
- - dbk:wordsize (String) 
- - dbk:xreflabel (String) 
- - xml:base (String) 
- - xml:id (String) 
- - xml:lang (String) 
-
-[dbk:term] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:termdef] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:baseform (String) 
- - dbk:sortas (String) 
-
-[dbk:tertiary] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:sortas (String) 
-
-[dbk:tertiaryie] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:linkends (String) 
-
-[dbk:textdata] > argeodbk:base
- + dbk:info (dbk:info) = dbk:info
- - dbk:encoding (String) 
- - dbk:entityref (String) 
- - dbk:fileref (String) 
- - dbk:format (String) 
-
-[dbk:textobject] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:info (dbk:info) = dbk:info
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:phrase (dbk:phrase) = dbk:phrase
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:textdata (dbk:textdata) = dbk:textdata
-
-[dbk:tfoot] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:colspec (dbk:colspec) = dbk:colspec *
- + dbk:row (dbk:row) = dbk:row *
- + dbk:tr (dbk:tr) = dbk:tr *
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:class (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:style (String) 
- - dbk:title (String) 
- - dbk:valign (String) 
-
-[dbk:tgroup] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:colspec (dbk:colspec) = dbk:colspec *
- + dbk:spanspec (dbk:spanspec) = dbk:spanspec *
- + dbk:tbody (dbk:tbody) = dbk:tbody
- + dbk:tfoot (dbk:tfoot) = dbk:tfoot
- + dbk:thead (dbk:thead) = dbk:thead
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:cols (String) 
- - dbk:colsep (String) 
- - dbk:rowsep (String) 
- - dbk:tgroupstyle (String) 
-
-[dbk:th] > argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- - dbk:abbr (String) 
- - dbk:align (String) 
- - dbk:annotations (String) 
- - dbk:arch (String) 
- - dbk:audience (String) 
- - dbk:axis (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:class (String) 
- - dbk:colspan (String) 
- - dbk:condition (String) 
- - dbk:conformance (String) 
- - dbk:dir (String) 
- - dbk:headers (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:os (String) 
- - dbk:remap (String) 
- - dbk:revision (String) 
- - dbk:revisionflag (String) 
- - dbk:rowspan (String) 
- - dbk:scope (String) 
- - dbk:security (String) 
- - dbk:style (String) 
- - dbk:title (String) 
- - dbk:userlevel (String) 
- - dbk:valign (String) 
- - dbk:vendor (String) 
- - dbk:version (String) 
- - dbk:wordsize (String) 
- - dbk:xreflabel (String) 
- - xml:base (String) 
- - xml:id (String) 
- - xml:lang (String) 
-
-[dbk:thead] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:colspec (dbk:colspec) = dbk:colspec *
- + dbk:row (dbk:row) = dbk:row *
- + dbk:tr (dbk:tr) = dbk:tr *
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:class (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:style (String) 
- - dbk:title (String) 
- - dbk:valign (String) 
-
-[dbk:tip] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:title] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:titleabbrev] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:toc] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:tocdiv (dbk:tocdiv) = dbk:tocdiv *
- + dbk:tocentry (dbk:tocentry) = dbk:tocentry *
-
-[dbk:tocdiv] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- + dbk:tocdiv (dbk:tocdiv) = dbk:tocdiv *
- + dbk:tocentry (dbk:tocentry) = dbk:tocentry *
- - dbk:pagenum (String) 
-
-[dbk:tocentry] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:pagenum (String) 
-
-[dbk:token] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:tr] > nt:base
- + dbk:td (dbk:td) = dbk:td *
- + dbk:th (dbk:th) = dbk:th *
- - dbk:align (String) 
- - dbk:annotations (String) 
- - dbk:arch (String) 
- - dbk:audience (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:class (String) 
- - dbk:condition (String) 
- - dbk:conformance (String) 
- - dbk:dir (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:os (String) 
- - dbk:remap (String) 
- - dbk:revision (String) 
- - dbk:revisionflag (String) 
- - dbk:security (String) 
- - dbk:style (String) 
- - dbk:title (String) 
- - dbk:userlevel (String) 
- - dbk:valign (String) 
- - dbk:vendor (String) 
- - dbk:version (String) 
- - dbk:wordsize (String) 
- - dbk:xreflabel (String) 
- - xml:base (String) 
- - xml:id (String) 
- - xml:lang (String) 
-
-[dbk:trademark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
-
-[dbk:type] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:uri] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:type (String) 
-
-[dbk:userinput] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:ubiquitousInlines
- + dbk:accel (dbk:accel) = dbk:accel *
- + dbk:co (dbk:co) = dbk:co *
- + dbk:command (dbk:command) = dbk:command *
- + dbk:computeroutput (dbk:computeroutput) = dbk:computeroutput *
- + dbk:envar (dbk:envar) = dbk:envar *
- + dbk:filename (dbk:filename) = dbk:filename *
- + dbk:guibutton (dbk:guibutton) = dbk:guibutton *
- + dbk:guiicon (dbk:guiicon) = dbk:guiicon *
- + dbk:guilabel (dbk:guilabel) = dbk:guilabel *
- + dbk:guimenu (dbk:guimenu) = dbk:guimenu *
- + dbk:guimenuitem (dbk:guimenuitem) = dbk:guimenuitem *
- + dbk:guisubmenu (dbk:guisubmenu) = dbk:guisubmenu *
- + dbk:keycap (dbk:keycap) = dbk:keycap *
- + dbk:keycode (dbk:keycode) = dbk:keycode *
- + dbk:keycombo (dbk:keycombo) = dbk:keycombo *
- + dbk:keysym (dbk:keysym) = dbk:keysym *
- + dbk:menuchoice (dbk:menuchoice) = dbk:menuchoice *
- + dbk:mousebutton (dbk:mousebutton) = dbk:mousebutton *
- + dbk:nonterminal (dbk:nonterminal) = dbk:nonterminal *
- + dbk:option (dbk:option) = dbk:option *
- + dbk:optional (dbk:optional) = dbk:optional *
- + dbk:package (dbk:package) = dbk:package *
- + dbk:parameter (dbk:parameter) = dbk:parameter *
- + dbk:prompt (dbk:prompt) = dbk:prompt *
- + dbk:property (dbk:property) = dbk:property *
- + dbk:replaceable (dbk:replaceable) = dbk:replaceable *
- + dbk:shortcut (dbk:shortcut) = dbk:shortcut *
- + dbk:systemitem (dbk:systemitem) = dbk:systemitem *
- + dbk:termdef (dbk:termdef) = dbk:termdef *
- + dbk:userinput (dbk:userinput) = dbk:userinput *
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:varargs] > argeodbk:base, argeodbk:linkingAttributes
-
-[dbk:variablelist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
- + dbk:varlistentry (dbk:varlistentry) = dbk:varlistentry *
- - dbk:spacing (String) 
- - dbk:termlength (String) 
-
-[dbk:varlistentry] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:listitem (dbk:listitem) = dbk:listitem
- + dbk:term (dbk:term) = dbk:term *
-
-[dbk:varname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:videodata] > argeodbk:base
- + dbk:info (dbk:info) = dbk:info
- - dbk:align (String) 
- - dbk:contentdepth (String) 
- - dbk:contentwidth (String) 
- - dbk:depth (String) 
- - dbk:entityref (String) 
- - dbk:fileref (String) 
- - dbk:format (String) 
- - dbk:scale (String) 
- - dbk:scalefit (String) 
- - dbk:valign (String) 
- - dbk:width (String) 
-
-[dbk:videoobject] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:info (dbk:info) = dbk:info
- + dbk:videodata (dbk:videodata) = dbk:videodata
-
-[dbk:void] > argeodbk:base, argeodbk:linkingAttributes
-
-[dbk:volumenum] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:warning] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:bridgehead (dbk:bridgehead) = dbk:bridgehead *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:revhistory (dbk:revhistory) = dbk:revhistory *
- + dbk:screenshot (dbk:screenshot) = dbk:screenshot *
-
-[dbk:wordasword] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:xmltext] > nt:base
- - jcr:xmlcharacters (String) 
-
-[dbk:xref] > argeodbk:base, argeodbk:linkingAttributes
- - dbk:endterm (Reference) 
- - dbk:xrefstyle (String) 
-
-[dbk:year] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[xs:anyType] > nt:base
- + * (nt:base) 
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
- - * (undefined) 
-
-
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/docbook/docbook.cnd b/org.argeo.server.jcr/src/org/argeo/jcr/docbook/docbook.cnd
deleted file mode 100644 (file)
index c737cee..0000000
+++ /dev/null
@@ -1,531 +0,0 @@
-<dbk = 'http://docbook.org/ns/docbook'>
-<argeodbk = 'http://www.argeo.org/ns/argeodbk'>
-<xlink = 'http://www.w3.org/1999/xlink'>
-
-[argeodbk:titled]
-mixin
- + dbk:info (dbk:info) = dbk:info *
- + dbk:title (dbk:title) = dbk:title *
-
-[argeodbk:linkingAttributes]
-mixin
- - dbk:linkend (String)
- - xlink:actuate (String)
- - xlink:arcrole (String)
- - xlink:href (String)
- - xlink:role (String)
- - xlink:show (String)
- - xlink:title (String)
- - xlink:type (String)
-
-[argeodbk:freeText]
-mixin
- + dbk:phrase (dbk:phrase) = dbk:phrase *
- + dbk:replaceable (dbk:replaceable) = dbk:replaceable *
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[argeodbk:markupInlines]
-mixin
-
-[argeodbk:listElements]
-mixin
- + dbk:itemizedlist (dbk:itemizedlist) = dbk:itemizedlist *
- + dbk:orderedlist (dbk:orderedlist) = dbk:orderedlist *
- + dbk:simplelist (dbk:simplelist) = dbk:simplelist *
-
-[argeodbk:paragraphElements]
-mixin
- + dbk:para (dbk:para) = dbk:para *
-
-[argeodbk:indexingInlines]
-mixin
-
-[argeodbk:techDocElements]
-mixin
- + dbk:table (dbk:table) = dbk:table *
-
-[argeodbk:techDocInlines]
-mixin
-
-[argeodbk:publishingElements]
-mixin
-
-[argeodbk:ubiquitousInlines]
-mixin
- + dbk:alt (dbk:alt) = dbk:alt *
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:biblioref (dbk:biblioref) = dbk:biblioref *
- + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
- + dbk:link (dbk:link) = dbk:link *
- + dbk:olink (dbk:olink) = dbk:olink *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:subscript (dbk:subscript) = dbk:subscript *
- + dbk:superscript (dbk:superscript) = dbk:superscript *
- + dbk:xref (dbk:xref) = dbk:xref *
-
-[argeodbk:abstractSection]
-mixin
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:remark (dbk:remark) = dbk:remark *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String)
- - dbk:status (String)
-
-[argeodbk:bibliographyInlines]
-mixin
- + dbk:author (dbk:author) = dbk:author *
- + dbk:editor (dbk:editor) = dbk:editor *
- + dbk:orgname (dbk:orgname) = dbk:orgname *
- + dbk:personname (dbk:personname) = dbk:personname *
-
-[argeodbk:publishingInlines]
-mixin
- + dbk:emphasis (dbk:emphasis) = dbk:emphasis *
-
-[argeodbk:base]
-abstract
- - dbk:annotations (String)
- - dbk:arch (String)
- - dbk:audience (String)
- - dbk:condition (String)
- - dbk:conformance (String)
- - dbk:dir (String)
- - dbk:os (String)
- - dbk:remap (String)
- - dbk:revision (String)
- - dbk:revisionflag (String)
- - dbk:role (String)
- - dbk:security (String)
- - dbk:userlevel (String)
- - dbk:vendor (String)
- - dbk:version (String)
- - dbk:wordsize (String)
- - dbk:xreflabel (String)
-// - {http://www.w3.org/XML/1998/namespace}base (String)
-// - {http://www.w3.org/XML/1998/namespace}id (String)
-// - {http://www.w3.org/XML/1998/namespace}lang (String)
-
-[dbk:alt] > argeodbk:base
- + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
-
-[dbk:anchor] > argeodbk:base
-
-[dbk:annotation] > argeodbk:base, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- - dbk:annotates (String) 
-
-[dbk:article] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:section (dbk:section) = dbk:section *
- - dbk:class (String) 
-
-[dbk:audiodata] > argeodbk:base
- + dbk:info (dbk:info) = dbk:info
- - dbk:entityref (String) 
- - dbk:fileref (String) 
- - dbk:format (String) 
-
-[dbk:audioobject] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:audiodata (dbk:audiodata) = dbk:audiodata
- + dbk:info (dbk:info) = dbk:info
-
-[dbk:author] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
- + dbk:orgname (dbk:orgname) = dbk:orgname
- + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
- + dbk:personname (dbk:personname) = dbk:personname
-
-[dbk:biblioref] > argeodbk:base, argeodbk:linkingAttributes
- - dbk:begin (String) 
- - dbk:end (String) 
- - dbk:endterm (Reference) 
- - dbk:units (String) 
- - dbk:xrefstyle (String) 
-
-[dbk:book] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:article (dbk:article) = dbk:article *
- + dbk:chapter (dbk:chapter) = dbk:chapter *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:caption] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
- - dbk:class (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:style (String) 
- - dbk:title (String) 
-
-[dbk:chapter] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:section (dbk:section) = dbk:section *
-
-[dbk:colspec] > argeodbk:base, argeodbk:linkingAttributes
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:colname (String) 
- - dbk:colnum (String) 
- - dbk:colsep (String) 
- - dbk:colwidth (String) 
- - dbk:rowsep (String) 
-
-[dbk:editor] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
- + dbk:orgname (dbk:orgname) = dbk:orgname
- + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
- + dbk:personname (dbk:personname) = dbk:personname
-
-[dbk:emphasis] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:entry] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:colname (String) 
- - dbk:colsep (String) 
- - dbk:morerows (String) 
- - dbk:nameend (String) 
- - dbk:namest (String) 
- - dbk:rotate (String) 
- - dbk:rowsep (String) 
- - dbk:spanname (String) 
- - dbk:valign (String) 
-
-[dbk:entrytbl] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:colspec (dbk:colspec) = dbk:colspec *
- + dbk:spanspec (dbk:spanspec) = dbk:spanspec *
- + dbk:tbody (dbk:tbody) = dbk:tbody
- + dbk:thead (dbk:thead) = dbk:thead
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:colname (String) 
- - dbk:cols (String) 
- - dbk:colsep (String) 
- - dbk:nameend (String) 
- - dbk:namest (String) 
- - dbk:rowsep (String) 
- - dbk:spanname (String) 
- - dbk:tgroupstyle (String) 
-
-[dbk:imagedata] > argeodbk:base
- + dbk:info (dbk:info) = dbk:info
- - dbk:align (String) 
- - dbk:contentdepth (String) 
- - dbk:contentwidth (String) 
- - dbk:depth (String) 
- - dbk:entityref (String) 
- - dbk:fileref (String) 
- - dbk:format (String) 
- - dbk:scale (String) 
- - dbk:scalefit (String) 
- - dbk:valign (String) 
- - dbk:width (String) 
-
-[dbk:imageobject] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:imagedata (dbk:imagedata) = dbk:imagedata
- + dbk:info (dbk:info) = dbk:info
-
-[dbk:info] > argeodbk:base
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:author (dbk:author) = dbk:author *
- + dbk:editor (dbk:editor) = dbk:editor *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:orgname (dbk:orgname) = dbk:orgname *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- + dbk:title (dbk:title) = dbk:title *
-
-[dbk:inlinemediaobject] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:alt (dbk:alt) = dbk:alt
- + dbk:audioobject (dbk:audioobject) = dbk:audioobject *
- + dbk:imageobject (dbk:imageobject) = dbk:imageobject *
- + dbk:info (dbk:info) = dbk:info
- + dbk:textobject (dbk:textobject) = dbk:textobject *
- + dbk:videoobject (dbk:videoobject) = dbk:videoobject *
-
-[dbk:itemizedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:listitem (dbk:listitem) = dbk:listitem *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- - dbk:mark (String) 
- - dbk:spacing (String) 
-
-[dbk:link] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:endterm (Reference) 
- - dbk:xrefstyle (String) 
-
-[dbk:listitem] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- - dbk:override (String) 
-
-[dbk:mediaobject] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:alt (dbk:alt) = dbk:alt
- + dbk:audioobject (dbk:audioobject) = dbk:audioobject *
- + dbk:caption (dbk:caption) = dbk:caption
- + dbk:imageobject (dbk:imageobject) = dbk:imageobject *
- + dbk:info (dbk:info) = dbk:info
- + dbk:textobject (dbk:textobject) = dbk:textobject *
- + dbk:videoobject (dbk:videoobject) = dbk:videoobject *
-
-[dbk:olink] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- - dbk:localinfo (String) 
- - dbk:targetdoc (String) 
- - dbk:targetptr (String) 
- - dbk:type (String) 
- - dbk:xrefstyle (String) 
-
-[dbk:orderedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:listitem (dbk:listitem) = dbk:listitem *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:remark (dbk:remark) = dbk:remark *
- - dbk:continuation (String) 
- - dbk:inheritnum (String) 
- - dbk:numeration (String) 
- - dbk:spacing (String) 
- - dbk:startingnumber (String) 
-
-[dbk:orgdiv] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:orgname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
- - dbk:otherclass (String) 
-
-[dbk:para] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
- + dbk:info (dbk:info) = dbk:info *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
-
-[dbk:personblurb] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
-
-[dbk:personname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:phrase] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:remark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:replaceable] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
- - dbk:class (String) 
-
-[dbk:row] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:entry (dbk:entry) = dbk:entry *
- + dbk:entrytbl (dbk:entrytbl) = dbk:entrytbl *
- - dbk:rowsep (String) 
- - dbk:valign (String) 
-
-[dbk:section] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:section (dbk:section) = dbk:section *
-
-[dbk:set] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:book (dbk:book) = dbk:book *
- + dbk:set (dbk:set) = dbk:set *
- + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
- - dbk:label (String) 
- - dbk:status (String) 
-
-[dbk:simplelist] > argeodbk:base, argeodbk:linkingAttributes
- - dbk:columns (String) 
- - dbk:type (String) 
-
-[dbk:spanspec] > argeodbk:base, argeodbk:linkingAttributes
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:colsep (String) 
- - dbk:nameend (String) 
- - dbk:namest (String) 
- - dbk:rowsep (String) 
- - dbk:spanname (String) 
-
-[dbk:subscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:subtitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:superscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
-
-[dbk:table] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:titled
- + dbk:caption (dbk:caption) = dbk:caption
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:tbody (dbk:tbody) = dbk:tbody *
- + dbk:textobject (dbk:textobject) = dbk:textobject *
- + dbk:tfoot (dbk:tfoot) = dbk:tfoot
- + dbk:tgroup (dbk:tgroup) = dbk:tgroup *
- + dbk:thead (dbk:thead) = dbk:thead
- - dbk:border (String) 
- - dbk:cellpadding (String) 
- - dbk:cellspacing (String) 
- - dbk:class (String) 
- - dbk:colsep (String) 
- - dbk:floatstyle (String) 
- - dbk:frame (String) 
- - dbk:label (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:orient (String) 
- - dbk:pgwide (String) 
- - dbk:rowheader (String) 
- - dbk:rowsep (String) 
- - dbk:rules (String) 
- - dbk:shortentry (String) 
- - dbk:style (String) 
- - dbk:summary (String) 
- - dbk:tabstyle (String) 
- - dbk:title (String) 
- - dbk:tocentry (String) 
- - dbk:width (String) 
-
-[dbk:tbody] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:row (dbk:row) = dbk:row *
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:class (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:style (String) 
- - dbk:title (String) 
- - dbk:valign (String) 
-
-[dbk:textobject] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
- + dbk:anchor (dbk:anchor) = dbk:anchor *
- + dbk:annotation (dbk:annotation) = dbk:annotation *
- + dbk:info (dbk:info) = dbk:info
- + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
- + dbk:phrase (dbk:phrase) = dbk:phrase
- + dbk:remark (dbk:remark) = dbk:remark *
-
-[dbk:tfoot] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:colspec (dbk:colspec) = dbk:colspec *
- + dbk:row (dbk:row) = dbk:row *
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:class (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:style (String) 
- - dbk:title (String) 
- - dbk:valign (String) 
-
-[dbk:tgroup] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:colspec (dbk:colspec) = dbk:colspec *
- + dbk:spanspec (dbk:spanspec) = dbk:spanspec *
- + dbk:tbody (dbk:tbody) = dbk:tbody
- + dbk:tfoot (dbk:tfoot) = dbk:tfoot
- + dbk:thead (dbk:thead) = dbk:thead
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:cols (String) 
- - dbk:colsep (String) 
- - dbk:rowsep (String) 
- - dbk:tgroupstyle (String) 
-
-[dbk:thead] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:colspec (dbk:colspec) = dbk:colspec *
- + dbk:row (dbk:row) = dbk:row *
- - dbk:align (String) 
- - dbk:char (String) 
- - dbk:charoff (String) 
- - dbk:class (String) 
- - dbk:lang (String) 
- - dbk:onclick (String) 
- - dbk:ondblclick (String) 
- - dbk:onkeydown (String) 
- - dbk:onkeypress (String) 
- - dbk:onkeyup (String) 
- - dbk:onmousedown (String) 
- - dbk:onmousemove (String) 
- - dbk:onmouseout (String) 
- - dbk:onmouseover (String) 
- - dbk:onmouseup (String) 
- - dbk:style (String) 
- - dbk:title (String) 
- - dbk:valign (String) 
-
-[dbk:title] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
-
-[dbk:videodata] > argeodbk:base
- + dbk:info (dbk:info) = dbk:info
- - dbk:align (String) 
- - dbk:contentdepth (String) 
- - dbk:contentwidth (String) 
- - dbk:depth (String) 
- - dbk:entityref (String) 
- - dbk:fileref (String) 
- - dbk:format (String) 
- - dbk:scale (String) 
- - dbk:scalefit (String) 
- - dbk:valign (String) 
- - dbk:width (String) 
-
-[dbk:videoobject] > argeodbk:base, argeodbk:linkingAttributes
- + dbk:info (dbk:info) = dbk:info
- + dbk:videodata (dbk:videodata) = dbk:videodata
-
-[dbk:xmltext] > nt:base
- - jcr:xmlcharacters (String) 
-
-[dbk:xref] > argeodbk:base, argeodbk:linkingAttributes
- - dbk:endterm (Reference) 
- - dbk:xrefstyle (String) 
-
-
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileStore.java b/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileStore.java
deleted file mode 100644 (file)
index 32a3ecb..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-package org.argeo.jcr.fs;
-
-import java.io.IOException;
-import java.nio.file.FileStore;
-import java.nio.file.attribute.FileAttributeView;
-import java.nio.file.attribute.FileStoreAttributeView;
-
-public class JcrFileStore extends FileStore {
-
-       @Override
-       public String name() {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public String type() {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public boolean isReadOnly() {
-               // TODO Auto-generated method stub
-               return false;
-       }
-
-       @Override
-       public long getTotalSpace() throws IOException {
-               // TODO Auto-generated method stub
-               return 0;
-       }
-
-       @Override
-       public long getUsableSpace() throws IOException {
-               // TODO Auto-generated method stub
-               return 0;
-       }
-
-       @Override
-       public long getUnallocatedSpace() throws IOException {
-               // TODO Auto-generated method stub
-               return 0;
-       }
-
-       @Override
-       public boolean supportsFileAttributeView(
-                       Class<? extends FileAttributeView> type) {
-               // TODO Auto-generated method stub
-               return false;
-       }
-
-       @Override
-       public boolean supportsFileAttributeView(String name) {
-               // TODO Auto-generated method stub
-               return false;
-       }
-
-       @Override
-       public <V extends FileStoreAttributeView> V getFileStoreAttributeView(
-                       Class<V> type) {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Object getAttribute(String attribute) throws IOException {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileSystem.java b/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileSystem.java
deleted file mode 100644 (file)
index 40328e8..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-package org.argeo.jcr.fs;
-
-import java.io.IOException;
-import java.nio.file.FileStore;
-import java.nio.file.FileSystem;
-import java.nio.file.Path;
-import java.nio.file.PathMatcher;
-import java.nio.file.WatchService;
-import java.nio.file.attribute.UserPrincipalLookupService;
-import java.nio.file.spi.FileSystemProvider;
-import java.util.Set;
-
-import javax.jcr.Session;
-
-import org.argeo.jcr.JcrUtils;
-
-public class JcrFileSystem extends FileSystem {
-       private final JcrFileSystemProvider provider;
-       private final Session session;
-
-       public JcrFileSystem(JcrFileSystemProvider provider, Session session) {
-               super();
-               this.provider = provider;
-               this.session = session;
-       }
-
-       @Override
-       public FileSystemProvider provider() {
-               return provider;
-       }
-
-       @Override
-       public void close() throws IOException {
-               JcrUtils.logoutQuietly(session);
-       }
-
-       @Override
-       public boolean isOpen() {
-               return session.isLive();
-       }
-
-       @Override
-       public boolean isReadOnly() {
-               return false;
-       }
-
-       @Override
-       public String getSeparator() {
-               return "/";
-       }
-
-       @Override
-       public Iterable<Path> getRootDirectories() {
-               return null;
-       }
-
-       @Override
-       public Iterable<FileStore> getFileStores() {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Set<String> supportedFileAttributeViews() {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Path getPath(String first, String... more) {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public PathMatcher getPathMatcher(String syntaxAndPattern) {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public UserPrincipalLookupService getUserPrincipalLookupService() {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public WatchService newWatchService() throws IOException {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       public Session getSession() {
-               return session;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java b/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java
deleted file mode 100644 (file)
index 8ea4cca..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-package org.argeo.jcr.fs;
-
-import java.io.IOException;
-import java.net.URI;
-import java.nio.channels.SeekableByteChannel;
-import java.nio.file.AccessMode;
-import java.nio.file.CopyOption;
-import java.nio.file.DirectoryStream;
-import java.nio.file.DirectoryStream.Filter;
-import java.nio.file.FileStore;
-import java.nio.file.FileSystem;
-import java.nio.file.LinkOption;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.nio.file.attribute.FileAttribute;
-import java.nio.file.attribute.FileAttributeView;
-import java.nio.file.spi.FileSystemProvider;
-import java.util.Map;
-import java.util.Set;
-
-public class JcrFileSystemProvider extends FileSystemProvider {
-
-       @Override
-       public String getScheme() {
-               return "jcr";
-       }
-
-       @Override
-       public FileSystem newFileSystem(URI uri, Map<String, ?> env)
-                       throws IOException {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public FileSystem getFileSystem(URI uri) {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Path getPath(URI uri) {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public SeekableByteChannel newByteChannel(Path path,
-                       Set<? extends OpenOption> options, FileAttribute<?>... attrs)
-                       throws IOException {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public DirectoryStream<Path> newDirectoryStream(Path dir,
-                       Filter<? super Path> filter) throws IOException {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public void createDirectory(Path dir, FileAttribute<?>... attrs)
-                       throws IOException {
-               // TODO Auto-generated method stub
-
-       }
-
-       @Override
-       public void delete(Path path) throws IOException {
-               // TODO Auto-generated method stub
-
-       }
-
-       @Override
-       public void copy(Path source, Path target, CopyOption... options)
-                       throws IOException {
-               // TODO Auto-generated method stub
-
-       }
-
-       @Override
-       public void move(Path source, Path target, CopyOption... options)
-                       throws IOException {
-               // TODO Auto-generated method stub
-
-       }
-
-       @Override
-       public boolean isSameFile(Path path, Path path2) throws IOException {
-               // TODO Auto-generated method stub
-               return false;
-       }
-
-       @Override
-       public boolean isHidden(Path path) throws IOException {
-               // TODO Auto-generated method stub
-               return false;
-       }
-
-       @Override
-       public FileStore getFileStore(Path path) throws IOException {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public void checkAccess(Path path, AccessMode... modes) throws IOException {
-               // TODO Auto-generated method stub
-
-       }
-
-       @Override
-       public <V extends FileAttributeView> V getFileAttributeView(Path path,
-                       Class<V> type, LinkOption... options) {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public <A extends BasicFileAttributes> A readAttributes(Path path,
-                       Class<A> type, LinkOption... options) throws IOException {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Map<String, Object> readAttributes(Path path, String attributes,
-                       LinkOption... options) throws IOException {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public void setAttribute(Path path, String attribute, Object value,
-                       LinkOption... options) throws IOException {
-               // TODO Auto-generated method stub
-
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFsException.java b/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrFsException.java
deleted file mode 100644 (file)
index f214fdc..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.argeo.jcr.fs;
-
-
-/** Exception related to the JCR FS */
-public class JcrFsException extends RuntimeException {
-       private static final long serialVersionUID = -7973896038244922980L;
-
-       public JcrFsException(String message, Throwable e) {
-               super(message, e);
-       }
-
-       public JcrFsException(String message) {
-               super(message);
-       }
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrPath.java b/org.argeo.server.jcr/src/org/argeo/jcr/fs/JcrPath.java
deleted file mode 100644 (file)
index e252935..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-package org.argeo.jcr.fs;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URI;
-import java.nio.file.FileSystem;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.WatchEvent.Kind;
-import java.nio.file.WatchEvent.Modifier;
-import java.nio.file.WatchKey;
-import java.nio.file.WatchService;
-import java.util.Iterator;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-public class JcrPath implements Path {
-       private JcrFileSystem filesSystem;
-       private String path;
-
-       private Node node;
-
-       public JcrPath(JcrFileSystem filesSystem, Node node) {
-               super();
-               this.filesSystem = filesSystem;
-               this.node = node;
-       }
-
-       @Override
-       public FileSystem getFileSystem() {
-               return filesSystem;
-       }
-
-       @Override
-       public boolean isAbsolute() {
-               return path.startsWith("/");
-       }
-
-       @Override
-       public Path getRoot() {
-               try {
-                       return new JcrPath(filesSystem, node.getSession().getRootNode());
-               } catch (RepositoryException e) {
-                       throw new JcrFsException("Cannot get root", e);
-               }
-       }
-
-       @Override
-       public Path getFileName() {
-               return null;
-       }
-
-       @Override
-       public Path getParent() {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public int getNameCount() {
-               // TODO Auto-generated method stub
-               return 0;
-       }
-
-       @Override
-       public Path getName(int index) {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Path subpath(int beginIndex, int endIndex) {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public boolean startsWith(Path other) {
-               // TODO Auto-generated method stub
-               return false;
-       }
-
-       @Override
-       public boolean startsWith(String other) {
-               // TODO Auto-generated method stub
-               return false;
-       }
-
-       @Override
-       public boolean endsWith(Path other) {
-               // TODO Auto-generated method stub
-               return false;
-       }
-
-       @Override
-       public boolean endsWith(String other) {
-               // TODO Auto-generated method stub
-               return false;
-       }
-
-       @Override
-       public Path normalize() {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Path resolve(Path other) {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Path resolve(String other) {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Path resolveSibling(Path other) {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Path resolveSibling(String other) {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Path relativize(Path other) {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public URI toUri() {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Path toAbsolutePath() {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Path toRealPath(LinkOption... options) throws IOException {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public File toFile() {
-               throw new UnsupportedOperationException();
-       }
-
-       @Override
-       public WatchKey register(WatchService watcher, Kind<?>[] events,
-                       Modifier... modifiers) throws IOException {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public WatchKey register(WatchService watcher, Kind<?>... events)
-                       throws IOException {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public Iterator<Path> iterator() {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public int compareTo(Path other) {
-               // TODO Auto-generated method stub
-               return 0;
-       }
-
-       public Node getNode() {
-               if (!isAbsolute())// TODO default dir
-                       throw new JcrFsException("Cannot get node from relative path");
-               try {
-                       if (node == null)
-                               node = filesSystem.getSession().getNode(path);
-                       return node;
-               } catch (RepositoryException e) {
-                       throw new JcrFsException("Cannot get node", e);
-               }
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/proxy/AbstractUrlProxy.java b/org.argeo.server.jcr/src/org/argeo/jcr/proxy/AbstractUrlProxy.java
deleted file mode 100644 (file)
index 30369ce..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr.proxy;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-
-import javax.jcr.Binary;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.nodetype.NodeType;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.jcr.ArgeoJcrException;
-import org.argeo.jcr.JcrUtils;
-
-/** Base class for URL based proxys. */
-public abstract class AbstractUrlProxy implements ResourceProxy {
-       private final static Log log = LogFactory.getLog(AbstractUrlProxy.class);
-
-       private Repository jcrRepository;
-       private Session jcrAdminSession;
-       private String proxyWorkspace = "proxy";
-
-       protected abstract Node retrieve(Session session, String path);
-
-       void init() {
-               try {
-                       jcrAdminSession = JcrUtils.loginOrCreateWorkspace(jcrRepository,
-                                       proxyWorkspace);
-                       beforeInitSessionSave(jcrAdminSession);
-                       if (jcrAdminSession.hasPendingChanges())
-                               jcrAdminSession.save();
-               } catch (Exception e) {
-                       JcrUtils.discardQuietly(jcrAdminSession);
-                       throw new ArgeoJcrException("Cannot initialize Maven proxy", e);
-               }
-       }
-
-       /**
-        * Called before the (admin) session is saved at the end of the
-        * initialization. Does nothing by default, to be overridden.
-        */
-       protected void beforeInitSessionSave(Session session)
-                       throws RepositoryException {
-       }
-
-       void destroy() {
-               JcrUtils.logoutQuietly(jcrAdminSession);
-       }
-
-       /**
-        * Called before the (admin) session is logged out when resources are
-        * released. Does nothing by default, to be overridden.
-        */
-       protected void beforeDestroySessionLogout() throws RepositoryException {
-       }
-
-       public Node proxy(String path) {
-               // we open a JCR session with client credentials in order not to use the
-               // admin session in multiple thread or make it a bottleneck.
-               Node nodeAdmin = null;
-               Node nodeClient = null;
-               Session clientSession = null;
-               try {
-                       clientSession = jcrRepository.login(proxyWorkspace);
-                       if (!clientSession.itemExists(path)
-                                       || shouldUpdate(clientSession, path)) {
-                               nodeAdmin = retrieveAndSave(path);
-                               if (nodeAdmin != null)
-                                       nodeClient = clientSession.getNode(path);
-                       } else
-                               nodeClient = clientSession.getNode(path);
-                       return nodeClient;
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot proxy " + path, e);
-               } finally {
-                       if (nodeClient == null)
-                               JcrUtils.logoutQuietly(clientSession);
-               }
-       }
-
-       protected synchronized Node retrieveAndSave(String path) {
-               try {
-                       Node node = retrieve(jcrAdminSession, path);
-                       if (node == null)
-                               return null;
-                       jcrAdminSession.save();
-                       return node;
-               } catch (RepositoryException e) {
-                       JcrUtils.discardQuietly(jcrAdminSession);
-                       throw new ArgeoJcrException("Cannot retrieve and save " + path, e);
-               } finally {
-                       notifyAll();
-               }
-       }
-
-       /** Session is not saved */
-       protected synchronized Node proxyUrl(Session session, String remoteUrl,
-                       String path) throws RepositoryException {
-               Node node = null;
-               if (session.itemExists(path)) {
-                       // throw new ArgeoJcrException("Node " + path + " already exists");
-               }
-               InputStream in = null;
-               try {
-                       URL u = new URL(remoteUrl);
-                       in = u.openStream();
-                       node = importFile(session, path, in);
-               } catch (IOException e) {
-                       if (log.isDebugEnabled()) {
-                               log.debug("Cannot read " + remoteUrl + ", skipping... "
-                                               + e.getMessage());
-                               // log.trace("Cannot read because of ", e);
-                       }
-                       JcrUtils.discardQuietly(session);
-               } finally {
-                       IOUtils.closeQuietly(in);
-               }
-               return node;
-       }
-
-       protected synchronized Node importFile(Session session, String path,
-                       InputStream in) throws RepositoryException {
-               Binary binary = null;
-               try {
-                       Node content = null;
-                       Node node = null;
-                       if (!session.itemExists(path)) {
-                               node = JcrUtils.mkdirs(session, path, NodeType.NT_FILE,
-                                               NodeType.NT_FOLDER, false);
-                               content = node.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
-                       } else {
-                               node = session.getNode(path);
-                               content = node.getNode(Node.JCR_CONTENT);
-                       }
-                       binary = session.getValueFactory().createBinary(in);
-                       content.setProperty(Property.JCR_DATA, binary);
-                       JcrUtils.updateLastModifiedAndParents(node, null);
-                       return node;
-               } finally {
-                       JcrUtils.closeQuietly(binary);
-               }
-       }
-
-       /** Whether the file should be updated. */
-       protected Boolean shouldUpdate(Session clientSession, String nodePath) {
-               return false;
-       }
-
-       public void setJcrRepository(Repository jcrRepository) {
-               this.jcrRepository = jcrRepository;
-       }
-
-       public void setProxyWorkspace(String localWorkspace) {
-               this.proxyWorkspace = localWorkspace;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/proxy/ResourceProxy.java b/org.argeo.server.jcr/src/org/argeo/jcr/proxy/ResourceProxy.java
deleted file mode 100644 (file)
index b4fb332..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr.proxy;
-
-import javax.jcr.Node;
-
-/** A proxy which nows how to resolve and synchronize relative URLs */
-public interface ResourceProxy {
-       /**
-        * Proxy the file referenced by this relative path in the underlying
-        * repository. A new session is created by each call, so the underlying
-        * session of the returned node must be closed by the caller.
-        * 
-        * @return the proxied Node, <code>null</code> if the resource was not found
-        *         (e.g. HTTP 404)
-        */
-       public Node proxy(String relativePath);
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/proxy/ResourceProxyServlet.java b/org.argeo.server.jcr/src/org/argeo/jcr/proxy/ResourceProxyServlet.java
deleted file mode 100644 (file)
index e92e2a4..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr.proxy;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.jcr.Binary;
-import javax.jcr.Node;
-import javax.jcr.PathNotFoundException;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.jcr.ArgeoJcrException;
-import org.argeo.jcr.JcrUtils;
-
-/** Wraps a proxy via HTTP */
-public class ResourceProxyServlet extends HttpServlet {
-       private static final long serialVersionUID = -8886549549223155801L;
-
-       private final static Log log = LogFactory
-                       .getLog(ResourceProxyServlet.class);
-
-       private ResourceProxy proxy;
-
-       private String contentTypeCharset = "UTF-8";
-
-       @Override
-       protected void doGet(HttpServletRequest request,
-                       HttpServletResponse response) throws ServletException, IOException {
-               String path = request.getPathInfo();
-
-               if (log.isTraceEnabled()) {
-                       log.trace("path=" + path);
-                       log.trace("UserPrincipal = " + request.getUserPrincipal().getName());
-                       log.trace("SessionID = " + request.getSession().getId());
-                       log.trace("ContextPath = " + request.getContextPath());
-                       log.trace("ServletPath = " + request.getServletPath());
-                       log.trace("PathInfo = " + request.getPathInfo());
-                       log.trace("Method = " + request.getMethod());
-                       log.trace("User-Agent = " + request.getHeader("User-Agent"));
-               }
-
-               Node node = null;
-               try {
-                       node = proxy.proxy(path);
-                       if (node == null)
-                               response.sendError(404);
-                       else
-                               processResponse(node, response);
-               } finally {
-                       if (node != null)
-                               try {
-                                       JcrUtils.logoutQuietly(node.getSession());
-                               } catch (RepositoryException e) {
-                                       // silent
-                               }
-               }
-
-       }
-
-       /** Retrieve the content of the node. */
-       protected void processResponse(Node node, HttpServletResponse response) {
-               Binary binary = null;
-               InputStream in = null;
-               try {
-                       String fileName = node.getName();
-                       String ext = FilenameUtils.getExtension(fileName);
-
-                       // TODO use a more generic / standard approach
-                       // see http://svn.apache.org/viewvc/tomcat/trunk/conf/web.xml
-                       String contentType;
-                       if ("xml".equals(ext))
-                               contentType = "text/xml;charset=" + contentTypeCharset;
-                       else if ("jar".equals(ext))
-                               contentType = "application/java-archive";
-                       else if ("zip".equals(ext))
-                               contentType = "application/zip";
-                       else if ("gz".equals(ext))
-                               contentType = "application/x-gzip";
-                       else if ("bz2".equals(ext))
-                               contentType = "application/x-bzip2";
-                       else if ("tar".equals(ext))
-                               contentType = "application/x-tar";
-                       else if ("rpm".equals(ext))
-                               contentType = "application/x-redhat-package-manager";
-                       else
-                               contentType = "application/octet-stream";
-                       contentType = contentType + ";name=\"" + fileName + "\"";
-                       response.setHeader("Content-Disposition", "attachment; filename=\""
-                                       + fileName + "\"");
-                       response.setHeader("Expires", "0");
-                       response.setHeader("Cache-Control", "no-cache, must-revalidate");
-                       response.setHeader("Pragma", "no-cache");
-
-                       response.setContentType(contentType);
-
-                       try {
-                               binary = node.getNode(Property.JCR_CONTENT)
-                                               .getProperty(Property.JCR_DATA).getBinary();
-                       } catch (PathNotFoundException e) {
-                               log.error("Node " + node + " as no data under content");
-                               throw e;
-                       }
-                       in = binary.getStream();
-                       IOUtils.copy(in, response.getOutputStream());
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot download " + node, e);
-               } finally {
-                       IOUtils.closeQuietly(in);
-                       JcrUtils.closeQuietly(binary);
-               }
-       }
-
-       public void setProxy(ResourceProxy resourceProxy) {
-               this.proxy = resourceProxy;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/security/JcrAuthorizations.java b/org.argeo.server.jcr/src/org/argeo/jcr/security/JcrAuthorizations.java
deleted file mode 100644 (file)
index 9ad2492..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr.security;
-
-import java.security.Principal;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.security.AccessControlManager;
-import javax.jcr.security.Privilege;
-
-import org.argeo.jcr.ArgeoJcrException;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.util.security.SimplePrincipal;
-
-/** Apply authorizations to a JCR repository. */
-public class JcrAuthorizations implements Runnable {
-       // private final static Log log =
-       // LogFactory.getLog(JcrAuthorizations.class);
-
-       private Repository repository;
-       private String workspace = null;
-
-       private String securityWorkspace = "security";
-
-       /**
-        * key := privilege1,privilege2/path/to/node<br/>
-        * value := group1,group2,user1
-        */
-       private Map<String, String> principalPrivileges = new HashMap<String, String>();
-
-       public void run() {
-               String currentWorkspace = workspace;
-               Session session = null;
-               try {
-                       if (workspace != null && workspace.equals("*")) {
-                               session = repository.login();
-                               String[] workspaces = session.getWorkspace()
-                                               .getAccessibleWorkspaceNames();
-                               JcrUtils.logoutQuietly(session);
-                               for (String wksp : workspaces) {
-                                       currentWorkspace = wksp;
-                                       if (currentWorkspace.equals(securityWorkspace))
-                                               continue;
-                                       session = repository.login(currentWorkspace);
-                                       initAuthorizations(session);
-                                       JcrUtils.logoutQuietly(session);
-                               }
-                       } else {
-                               session = repository.login(workspace);
-                               initAuthorizations(session);
-                       }
-               } catch (Exception e) {
-                       JcrUtils.discardQuietly(session);
-                       throw new ArgeoJcrException(
-                                       "Cannot set authorizations " + principalPrivileges
-                                                       + " on workspace " + currentWorkspace, e);
-               } finally {
-                       JcrUtils.logoutQuietly(session);
-               }
-       }
-
-       protected void processWorkspace(String workspace) {
-               Session session = null;
-               try {
-                       session = repository.login(workspace);
-                       initAuthorizations(session);
-               } catch (Exception e) {
-                       JcrUtils.discardQuietly(session);
-                       throw new ArgeoJcrException("Cannot set authorizations "
-                                       + principalPrivileges + " on repository " + repository, e);
-               } finally {
-                       JcrUtils.logoutQuietly(session);
-               }
-       }
-
-       /** @deprecated call {@link #run()} instead. */
-       @Deprecated
-       public void init() {
-               run();
-       }
-
-       protected void initAuthorizations(Session session)
-                       throws RepositoryException {
-               AccessControlManager acm = session.getAccessControlManager();
-
-               for (String privileges : principalPrivileges.keySet()) {
-                       String path = null;
-                       int slashIndex = privileges.indexOf('/');
-                       if (slashIndex == 0) {
-                               throw new ArgeoJcrException("Privilege " + privileges
-                                               + " badly formatted it starts with /");
-                       } else if (slashIndex > 0) {
-                               path = privileges.substring(slashIndex);
-                               privileges = privileges.substring(0, slashIndex);
-                       }
-
-                       if (path == null)
-                               path = "/";
-
-                       List<Privilege> privs = new ArrayList<Privilege>();
-                       for (String priv : privileges.split(",")) {
-                               privs.add(acm.privilegeFromName(priv));
-                       }
-
-                       String principalNames = principalPrivileges.get(privileges);
-                       for (String principalName : principalNames.split(",")) {
-                               Principal principal = getOrCreatePrincipal(session,
-                                               principalName);
-                               JcrUtils.addPrivileges(session, path, principal, privs);
-                               // if (log.isDebugEnabled()) {
-                               // StringBuffer privBuf = new StringBuffer();
-                               // for (Privilege priv : privs)
-                               // privBuf.append(priv.getName());
-                               // log.debug("Added privileges " + privBuf + " to "
-                               // + principal.getName() + " on " + path + " in '"
-                               // + session.getWorkspace().getName() + "'");
-                               // }
-                       }
-               }
-
-               // if (log.isDebugEnabled())
-               // log.debug("JCR authorizations applied on '"
-               // + session.getWorkspace().getName() + "'");
-       }
-
-       /**
-        * Returns a {@link SimplePrincipal}, does not check whether it exists since
-        * such capabilities is not provided by the standard JCR API. Can be
-        * overridden to provide smarter handling
-        */
-       protected Principal getOrCreatePrincipal(Session session,
-                       String principalName) throws RepositoryException {
-               return new SimplePrincipal(principalName);
-       }
-
-       // public static void addPrivileges(Session session, Principal principal,
-       // String path, List<Privilege> privs) throws RepositoryException {
-       // AccessControlManager acm = session.getAccessControlManager();
-       // // search for an access control list
-       // AccessControlList acl = null;
-       // AccessControlPolicyIterator policyIterator = acm
-       // .getApplicablePolicies(path);
-       // if (policyIterator.hasNext()) {
-       // while (policyIterator.hasNext()) {
-       // AccessControlPolicy acp = policyIterator
-       // .nextAccessControlPolicy();
-       // if (acp instanceof AccessControlList)
-       // acl = ((AccessControlList) acp);
-       // }
-       // } else {
-       // AccessControlPolicy[] existingPolicies = acm.getPolicies(path);
-       // for (AccessControlPolicy acp : existingPolicies) {
-       // if (acp instanceof AccessControlList)
-       // acl = ((AccessControlList) acp);
-       // }
-       // }
-       //
-       // if (acl != null) {
-       // acl.addAccessControlEntry(principal,
-       // privs.toArray(new Privilege[privs.size()]));
-       // acm.setPolicy(path, acl);
-       // session.save();
-       // if (log.isDebugEnabled()) {
-       // StringBuffer buf = new StringBuffer("");
-       // for (int i = 0; i < privs.size(); i++) {
-       // if (i != 0)
-       // buf.append(',');
-       // buf.append(privs.get(i).getName());
-       // }
-       // log.debug("Added privilege(s) '" + buf + "' to '"
-       // + principal.getName() + "' on " + path
-       // + " from workspace '"
-       // + session.getWorkspace().getName() + "'");
-       // }
-       // } else {
-       // throw new ArgeoJcrException("Don't know how to apply  privileges "
-       // + privs + " to " + principal + " on " + path
-       // + " from workspace '" + session.getWorkspace().getName()
-       // + "'");
-       // }
-       // }
-
-       @Deprecated
-       public void setGroupPrivileges(Map<String, String> groupPrivileges) {
-               this.principalPrivileges = groupPrivileges;
-       }
-
-       public void setPrincipalPrivileges(Map<String, String> principalPrivileges) {
-               this.principalPrivileges = principalPrivileges;
-       }
-
-       public void setRepository(Repository repository) {
-               this.repository = repository;
-       }
-
-       public void setWorkspace(String workspace) {
-               this.workspace = workspace;
-       }
-
-       public void setSecurityWorkspace(String securityWorkspace) {
-               this.securityWorkspace = securityWorkspace;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/security/JcrKeyring.java b/org.argeo.server.jcr/src/org/argeo/jcr/security/JcrKeyring.java
deleted file mode 100644 (file)
index 8ab6ed3..0000000
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr.security;
-
-import java.io.ByteArrayInputStream;
-import java.io.CharArrayReader;
-import java.io.InputStream;
-import java.io.Reader;
-import java.security.Provider;
-import java.security.SecureRandom;
-
-import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
-import javax.jcr.Binary;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.commons.io.IOUtils;
-import org.argeo.jcr.ArgeoJcrException;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.ArgeoTypes;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.jcr.UserJcrUtils;
-import org.argeo.util.security.AbstractKeyring;
-import org.argeo.util.security.PBEKeySpecCallback;
-
-/** JCR based implementation of a keyring */
-public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
-       /**
-        * Stronger with 256, but causes problem with Oracle JVM, force 128 in this
-        * case
-        */
-       public final static Long DEFAULT_SECRETE_KEY_LENGTH = 256l;
-       public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
-       public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
-       public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding";
-
-       private Integer iterationCountFactor = 200;
-       private Long secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
-       private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
-       private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
-       private String cipherName = DEFAULT_CIPHER_NAME;
-
-       private Session session;
-
-       /**
-        * When setup is called the session has not yet been saved and we don't want
-        * to save it since there maybe other data which would be inconsistent. So
-        * we keep a reference to this node which will then be used (an reset to
-        * null) when handling the PBE callback. We keep one per thread in case
-        * multiple users are accessing the same instance of a keyring.
-        */
-       private ThreadLocal<Node> notYetSavedKeyring = new ThreadLocal<Node>() {
-
-               @Override
-               protected Node initialValue() {
-                       return null;
-               }
-       };
-
-       @Override
-       protected Boolean isSetup() {
-               try {
-                       if (notYetSavedKeyring.get() != null)
-                               return true;
-
-                       Node userHome = UserJcrUtils.getUserHome(session);
-                       return userHome.hasNode(ARGEO_KEYRING);
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot check whether keyring is setup", e);
-               }
-       }
-
-       @Override
-       protected void setup(char[] password) {
-               Binary binary = null;
-               InputStream in = null;
-               try {
-                       Node userHome = UserJcrUtils.getUserHome(session);
-                       if (userHome.hasNode(ARGEO_KEYRING))
-                               throw new ArgeoJcrException("Keyring already setup");
-                       Node keyring = userHome.addNode(ARGEO_KEYRING);
-                       keyring.addMixin(ArgeoTypes.ARGEO_PBE_SPEC);
-
-                       // deterministic salt and iteration count based on username
-                       String username = session.getUserID();
-                       byte[] salt = new byte[8];
-                       byte[] usernameBytes = username.getBytes();
-                       for (int i = 0; i < salt.length; i++) {
-                               if (i < usernameBytes.length)
-                                       salt[i] = usernameBytes[i];
-                               else
-                                       salt[i] = 0;
-                       }
-                       in = new ByteArrayInputStream(salt);
-                       binary = session.getValueFactory().createBinary(in);
-                       keyring.setProperty(ARGEO_SALT, binary);
-
-                       Integer iterationCount = username.length() * iterationCountFactor;
-                       keyring.setProperty(ARGEO_ITERATION_COUNT, iterationCount);
-
-                       // default algo
-                       // TODO check if algo and key length are available, use DES if not
-                       keyring.setProperty(ARGEO_SECRET_KEY_FACTORY, secreteKeyFactoryName);
-                       keyring.setProperty(ARGEO_KEY_LENGTH, secreteKeyLength);
-                       keyring.setProperty(ARGEO_SECRET_KEY_ENCRYPTION, secreteKeyEncryption);
-                       keyring.setProperty(ARGEO_CIPHER, cipherName);
-
-                       // keyring.getSession().save();
-
-                       // encrypted password hash
-                       // IOUtils.closeQuietly(in);
-                       // JcrUtils.closeQuietly(binary);
-                       // byte[] btPass = hash(password, salt, iterationCount);
-                       // in = new ByteArrayInputStream(btPass);
-                       // binary = session.getValueFactory().createBinary(in);
-                       // keyring.setProperty(ARGEO_PASSWORD, binary);
-
-                       notYetSavedKeyring.set(keyring);
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot setup keyring", e);
-               } finally {
-                       JcrUtils.closeQuietly(binary);
-                       IOUtils.closeQuietly(in);
-                       // JcrUtils.discardQuietly(session);
-               }
-       }
-
-       @Override
-       protected void handleKeySpecCallback(PBEKeySpecCallback pbeCallback) {
-               try {
-                       Node userHome = UserJcrUtils.getUserHome(session);
-                       Node keyring;
-                       if (userHome.hasNode(ARGEO_KEYRING))
-                               keyring = userHome.getNode(ARGEO_KEYRING);
-                       else if (notYetSavedKeyring.get() != null)
-                               keyring = notYetSavedKeyring.get();
-                       else
-                               throw new ArgeoJcrException("Keyring not setup");
-
-                       pbeCallback.set(keyring.getProperty(ARGEO_SECRET_KEY_FACTORY).getString(),
-                                       JcrUtils.getBinaryAsBytes(keyring.getProperty(ARGEO_SALT)),
-                                       (int) keyring.getProperty(ARGEO_ITERATION_COUNT).getLong(),
-                                       (int) keyring.getProperty(ARGEO_KEY_LENGTH).getLong(),
-                                       keyring.getProperty(ARGEO_SECRET_KEY_ENCRYPTION).getString());
-
-                       if (notYetSavedKeyring.get() != null)
-                               notYetSavedKeyring.remove();
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot handle key spec callback", e);
-               }
-       }
-
-       /** The parent node must already exist at this path. */
-       @Override
-       protected synchronized void encrypt(String path, InputStream unencrypted) {
-               // should be called first for lazy initialization
-               SecretKey secretKey = getSecretKey();
-
-               Binary binary = null;
-               InputStream in = null;
-               try {
-                       Cipher cipher = createCipher();
-                       Node node;
-                       if (!session.nodeExists(path)) {
-                               String parentPath = JcrUtils.parentPath(path);
-                               if (!session.nodeExists(parentPath))
-                                       throw new ArgeoJcrException("No parent node of " + path);
-                               Node parentNode = session.getNode(parentPath);
-                               node = parentNode.addNode(JcrUtils.nodeNameFromPath(path));
-                       } else {
-                               node = session.getNode(path);
-                       }
-                       node.addMixin(ArgeoTypes.ARGEO_ENCRYPTED);
-                       SecureRandom random = new SecureRandom();
-                       byte[] iv = new byte[16];
-                       random.nextBytes(iv);
-                       cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
-                       JcrUtils.setBinaryAsBytes(node, ARGEO_IV, iv);
-
-                       in = new CipherInputStream(unencrypted, cipher);
-                       binary = session.getValueFactory().createBinary(in);
-                       node.setProperty(Property.JCR_DATA, binary);
-                       session.save();
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot encrypt", e);
-               } finally {
-                       IOUtils.closeQuietly(unencrypted);
-                       IOUtils.closeQuietly(in);
-                       JcrUtils.closeQuietly(binary);
-               }
-       }
-
-       @Override
-       protected synchronized InputStream decrypt(String path) {
-               Binary binary = null;
-               InputStream encrypted = null;
-               Reader reader = null;
-               try {
-                       if (!session.nodeExists(path)) {
-                               char[] password = ask();
-                               reader = new CharArrayReader(password);
-                               return new ByteArrayInputStream(IOUtils.toByteArray(reader));
-                       } else {
-                               // should be called first for lazy initialisation
-                               SecretKey secretKey = getSecretKey();
-
-                               Cipher cipher = createCipher();
-
-                               Node node = session.getNode(path);
-                               if (node.hasProperty(ARGEO_IV)) {
-                                       byte[] iv = JcrUtils.getBinaryAsBytes(node.getProperty(ARGEO_IV));
-                                       cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
-                               } else {
-                                       cipher.init(Cipher.DECRYPT_MODE, secretKey);
-                               }
-
-                               binary = node.getProperty(Property.JCR_DATA).getBinary();
-                               encrypted = binary.getStream();
-                               return new CipherInputStream(encrypted, cipher);
-                       }
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot decrypt", e);
-               } finally {
-                       IOUtils.closeQuietly(encrypted);
-                       IOUtils.closeQuietly(reader);
-                       JcrUtils.closeQuietly(binary);
-               }
-       }
-
-       protected Cipher createCipher() {
-               try {
-                       Node userHome = UserJcrUtils.getUserHome(session);
-                       if (!userHome.hasNode(ARGEO_KEYRING))
-                               throw new ArgeoJcrException("Keyring not setup");
-                       Node keyring = userHome.getNode(ARGEO_KEYRING);
-                       String cipherName = keyring.getProperty(ARGEO_CIPHER).getString();
-                       Provider securityProvider = getSecurityProvider();
-                       Cipher cipher;
-                       if (securityProvider == null)// TODO use BC?
-                               cipher = Cipher.getInstance(cipherName);
-                       else
-                               cipher = Cipher.getInstance(cipherName, securityProvider);
-                       return cipher;
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot get cipher", e);
-               }
-       }
-
-       public synchronized void changePassword(char[] oldPassword, char[] newPassword) {
-               // TODO decrypt with old pw / encrypt with new pw all argeo:encrypted
-       }
-
-       public synchronized void setSession(Session session) {
-               this.session = session;
-       }
-
-       public void setIterationCountFactor(Integer iterationCountFactor) {
-               this.iterationCountFactor = iterationCountFactor;
-       }
-
-       public void setSecreteKeyLength(Long keyLength) {
-               this.secreteKeyLength = keyLength;
-       }
-
-       public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
-               this.secreteKeyFactoryName = secreteKeyFactoryName;
-       }
-
-       public void setSecreteKeyEncryption(String secreteKeyEncryption) {
-               this.secreteKeyEncryption = secreteKeyEncryption;
-       }
-
-       public void setCipherName(String cipherName) {
-               this.cipherName = cipherName;
-       }
-
-}
\ No newline at end of file
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/spring/ThreadBoundSession.java b/org.argeo.server.jcr/src/org/argeo/jcr/spring/ThreadBoundSession.java
deleted file mode 100644 (file)
index 35f0215..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr.spring;
-
-import org.argeo.jcr.ThreadBoundJcrSessionFactory;
-import org.springframework.beans.factory.DisposableBean;
-import org.springframework.beans.factory.FactoryBean;
-import org.springframework.beans.factory.InitializingBean;
-
-@SuppressWarnings("rawtypes")
-@Deprecated
-public class ThreadBoundSession extends ThreadBoundJcrSessionFactory implements FactoryBean, InitializingBean, DisposableBean{
-       public void afterPropertiesSet() throws Exception {
-               init();
-       }
-
-       public void destroy() throws Exception {
-               dispose();
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/tabular/JcrTabularRowIterator.java b/org.argeo.server.jcr/src/org/argeo/jcr/tabular/JcrTabularRowIterator.java
deleted file mode 100644 (file)
index d4ffbf8..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr.tabular;
-
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ArrayBlockingQueue;
-
-import javax.jcr.Binary;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Property;
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.io.IOUtils;
-import org.argeo.jcr.ArgeoJcrException;
-import org.argeo.jcr.ArgeoTypes;
-import org.argeo.util.CsvParser;
-import org.argeo.util.tabular.ArrayTabularRow;
-import org.argeo.util.tabular.TabularColumn;
-import org.argeo.util.tabular.TabularRow;
-import org.argeo.util.tabular.TabularRowIterator;
-
-/** Iterates over the rows of a {@link ArgeoTypes#ARGEO_TABLE} node. */
-public class JcrTabularRowIterator implements TabularRowIterator {
-       private Boolean hasNext = null;
-       private Boolean parsingCompleted = false;
-
-       private Long currentRowNumber = 0l;
-
-       private List<TabularColumn> header = new ArrayList<TabularColumn>();
-
-       /** referenced so that we can close it */
-       private Binary binary;
-       private InputStream in;
-
-       private CsvParser csvParser;
-       private ArrayBlockingQueue<List<String>> textLines;
-
-       public JcrTabularRowIterator(Node tableNode) {
-               try {
-                       for (NodeIterator it = tableNode.getNodes(); it.hasNext();) {
-                               Node node = it.nextNode();
-                               if (node.isNodeType(ArgeoTypes.ARGEO_COLUMN)) {
-                                       Integer type = PropertyType.valueFromName(node.getProperty(
-                                                       Property.JCR_REQUIRED_TYPE).getString());
-                                       TabularColumn tc = new TabularColumn(node.getProperty(
-                                                       Property.JCR_TITLE).getString(), type);
-                                       header.add(tc);
-                               }
-                       }
-                       Node contentNode = tableNode.getNode(Property.JCR_CONTENT);
-                       if (contentNode.isNodeType(ArgeoTypes.ARGEO_CSV)) {
-                               textLines = new ArrayBlockingQueue<List<String>>(1000);
-                               csvParser = new CsvParser() {
-                                       protected void processLine(Integer lineNumber,
-                                                       List<String> header, List<String> tokens) {
-                                               try {
-                                                       textLines.put(tokens);
-                                               } catch (InterruptedException e) {
-                                                       // TODO Auto-generated catch block
-                                                       e.printStackTrace();
-                                               }
-                                               // textLines.add(tokens);
-                                               if (hasNext == null) {
-                                                       hasNext = true;
-                                                       synchronized (JcrTabularRowIterator.this) {
-                                                               JcrTabularRowIterator.this.notifyAll();
-                                                       }
-                                               }
-                                       }
-                               };
-                               csvParser.setNoHeader(true);
-                               binary = contentNode.getProperty(Property.JCR_DATA).getBinary();
-                               in = binary.getStream();
-                               Thread thread = new Thread(contentNode.getPath() + " reader") {
-                                       public void run() {
-                                               try {
-                                                       csvParser.parse(in);
-                                               } finally {
-                                                       parsingCompleted = true;
-                                                       IOUtils.closeQuietly(in);
-                                               }
-                                       }
-                               };
-                               thread.start();
-                       }
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot read table " + tableNode, e);
-               }
-       }
-
-       public synchronized boolean hasNext() {
-               // we don't know if there is anything available
-               // while (hasNext == null)
-               // try {
-               // wait();
-               // } catch (InterruptedException e) {
-               // // silent
-               // // FIXME better deal with interruption
-               // Thread.currentThread().interrupt();
-               // break;
-               // }
-
-               // buffer not empty
-               if (!textLines.isEmpty())
-                       return true;
-
-               // maybe the parsing is finished but the flag has not been set
-               while (!parsingCompleted && textLines.isEmpty())
-                       try {
-                               wait(100);
-                       } catch (InterruptedException e) {
-                               // silent
-                               // FIXME better deal with interruption
-                               Thread.currentThread().interrupt();
-                               break;
-                       }
-
-               // buffer not empty
-               if (!textLines.isEmpty())
-                       return true;
-
-               // (parsingCompleted && textLines.isEmpty())
-               return false;
-
-               // if (!hasNext && textLines.isEmpty()) {
-               // if (in != null) {
-               // IOUtils.closeQuietly(in);
-               // in = null;
-               // }
-               // if (binary != null) {
-               // JcrUtils.closeQuietly(binary);
-               // binary = null;
-               // }
-               // return false;
-               // } else
-               // return true;
-       }
-
-       public synchronized TabularRow next() {
-               try {
-                       List<String> tokens = textLines.take();
-                       List<Object> objs = new ArrayList<Object>(tokens.size());
-                       for (String token : tokens) {
-                               // TODO convert to other formats using header
-                               objs.add(token);
-                       }
-                       currentRowNumber++;
-                       return new ArrayTabularRow(objs);
-               } catch (InterruptedException e) {
-                       // silent
-                       // FIXME better deal with interruption
-               }
-               return null;
-       }
-
-       public void remove() {
-               throw new UnsupportedOperationException();
-       }
-
-       public Long getCurrentRowNumber() {
-               return currentRowNumber;
-       }
-
-       public List<TabularColumn> getHeader() {
-               return header;
-       }
-
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/tabular/JcrTabularWriter.java b/org.argeo.server.jcr/src/org/argeo/jcr/tabular/JcrTabularWriter.java
deleted file mode 100644 (file)
index c3fd97c..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr.tabular;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.util.List;
-
-import javax.jcr.Binary;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.io.IOUtils;
-import org.argeo.jcr.ArgeoJcrException;
-import org.argeo.jcr.ArgeoTypes;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.util.CsvWriter;
-import org.argeo.util.tabular.TabularColumn;
-import org.argeo.util.tabular.TabularWriter;
-
-/** Write / reference tabular content in a JCR repository. */
-public class JcrTabularWriter implements TabularWriter {
-       private Node contentNode;
-       private ByteArrayOutputStream out;
-       private CsvWriter csvWriter;
-       
-       @SuppressWarnings("unused")
-       private final List<TabularColumn> columns;
-
-       /** Creates a table node */
-       public JcrTabularWriter(Node tableNode, List<TabularColumn> columns,
-                       String contentNodeType) {
-               try {
-                       this.columns = columns;
-                       for (TabularColumn column : columns) {
-                               String normalized = JcrUtils.replaceInvalidChars(column
-                                               .getName());
-                               Node columnNode = tableNode.addNode(normalized,
-                                               ArgeoTypes.ARGEO_COLUMN);
-                               columnNode.setProperty(Property.JCR_TITLE, column.getName());
-                               if (column.getType() != null)
-                                       columnNode.setProperty(Property.JCR_REQUIRED_TYPE,
-                                                       PropertyType.nameFromValue(column.getType()));
-                               else
-                                       columnNode.setProperty(Property.JCR_REQUIRED_TYPE,
-                                                       PropertyType.TYPENAME_STRING);
-                       }
-                       contentNode = tableNode.addNode(Property.JCR_CONTENT,
-                                       contentNodeType);
-                       if (contentNodeType.equals(ArgeoTypes.ARGEO_CSV)) {
-                               contentNode.setProperty(Property.JCR_MIMETYPE, "text/csv");
-                               contentNode.setProperty(Property.JCR_ENCODING, "UTF-8");
-                               out = new ByteArrayOutputStream();
-                               csvWriter = new CsvWriter(out);
-                       }
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot create table node " + tableNode, e);
-               }
-       }
-
-       public void appendRow(Object[] row) {
-               csvWriter.writeLine(row);
-       }
-
-       public void close() {
-               Binary binary = null;
-               InputStream in = null;
-               try {
-                       // TODO parallelize with pipes and writing from another thread
-                       in = new ByteArrayInputStream(out.toByteArray());
-                       binary = contentNode.getSession().getValueFactory()
-                                       .createBinary(in);
-                       contentNode.setProperty(Property.JCR_DATA, binary);
-               } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot store data in " + contentNode, e);
-               } finally {
-                       IOUtils.closeQuietly(in);
-                       JcrUtils.closeQuietly(binary);
-               }
-       }
-}
diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/unit/AbstractJcrTestCase.java b/org.argeo.server.jcr/src/org/argeo/jcr/unit/AbstractJcrTestCase.java
deleted file mode 100644 (file)
index 1269a3e..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.jcr.unit;
-
-import java.io.File;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-
-import javax.jcr.Repository;
-import javax.jcr.Session;
-import javax.jcr.SimpleCredentials;
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.jcr.ArgeoJcrException;
-
-import junit.framework.TestCase;
-
-public abstract class AbstractJcrTestCase extends TestCase {
-       private final static Log log = LogFactory.getLog(AbstractJcrTestCase.class);
-
-       private Repository repository;
-       private Session session = null;
-
-       public final static String LOGIN_CONTEXT_TEST_SYSTEM = "TEST_JACKRABBIT_ADMIN";
-
-       // protected abstract File getRepositoryFile() throws Exception;
-
-       protected abstract Repository createRepository() throws Exception;
-
-       protected abstract void clearRepository(Repository repository)
-                       throws Exception;
-
-       @Override
-       protected void setUp() throws Exception {
-               File homeDir = getHomeDir();
-               FileUtils.deleteDirectory(homeDir);
-               repository = createRepository();
-       }
-
-       @Override
-       protected void tearDown() throws Exception {
-               if (session != null) {
-                       session.logout();
-                       if (log.isTraceEnabled())
-                               log.trace("Logout session");
-               }
-               clearRepository(repository);
-       }
-
-       protected Session session() {
-               if (session != null && session.isLive())
-                       return session;
-               Session session;
-               if (getLoginContext() != null) {
-                       LoginContext lc;
-                       try {
-                               lc = new LoginContext(getLoginContext());
-                               lc.login();
-                       } catch (LoginException e) {
-                               throw new ArgeoJcrException("JAAS login failed", e);
-                       }
-                       session = Subject.doAs(lc.getSubject(),
-                                       new PrivilegedAction<Session>() {
-
-                                               @Override
-                                               public Session run() {
-                                                       return login();
-                                               }
-
-                                       });
-               } else
-                       session = login();
-               this.session = session;
-               return this.session;
-       }
-
-       protected String getLoginContext() {
-               return null;
-       }
-
-       protected Session login() {
-               try {
-                       if (log.isTraceEnabled())
-                               log.trace("Login session");
-                       Subject subject = Subject.getSubject(AccessController.getContext());
-                       if (subject != null)
-                               return getRepository().login();
-                       else
-                               return getRepository().login(
-                                               new SimpleCredentials("demo", "demo".toCharArray()));
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot login to repository", e);
-               }
-       }
-
-       protected Repository getRepository() {
-               return repository;
-       }
-
-       /**
-        * enables children class to set an existing repository in case it is not
-        * deleted on startup, to test migration by instance
-        */
-       public void setRepository(Repository repository) {
-               this.repository = repository;
-       }
-
-       protected File getHomeDir() {
-               File homeDir = new File(System.getProperty("java.io.tmpdir"),
-                               AbstractJcrTestCase.class.getSimpleName() + "-"
-                                               + System.getProperty("user.name"));
-               return homeDir;
-       }
-
-}
diff --git a/pom.xml b/pom.xml
index 92be301deda04d0c79858674ef6d7625edb13996..3f8cc338452170399f01f02cbf213cce2c2c785f 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>org.argeo.commons</groupId>
        <artifactId>argeo-commons</artifactId>
        <modules>
                <!-- Base -->
                <module>org.argeo.util</module>
+               <module>org.argeo.enterprise</module>
+               <module>org.argeo.jcr</module>
                <module>org.argeo.osgi.boot</module>
-               <module>org.argeo.server.jcr</module>
-               <!-- Security -->
-               <module>org.argeo.security.core</module>
                <!-- Eclipse -->
                <module>org.argeo.eclipse.ui</module>
                <module>org.argeo.eclipse.ui.rap</module>
+               <module>org.argeo.eclipse.ui.workbench</module>
                <!-- CMS -->
-               <module>org.argeo.cms.api</module>
+               <module>org.argeo.node.api</module>
                <module>org.argeo.cms</module>
-               <module>org.argeo.security.jackrabbit</module>
-               <!-- Workbench -->
-               <module>org.argeo.eclipse.ui.workbench</module>
-               <module>org.argeo.eclipse.ui.workbench.rap</module>
+               <!-- CMS Workbench -->
                <module>org.argeo.security.ui</module>
-               <module>org.argeo.security.ui.admin</module>
-               <module>org.argeo.security.ui.rap</module>
+               <module>org.argeo.cms.ui.workbench</module>
+               <module>org.argeo.cms.ui.workbench.rap</module>
+               <!-- Third Parties Extensions -->
+               <module>org.argeo.ext.jackrabbit</module>
+               <module>org.argeo.ext.rap.ui.workbench</module>
                <!-- Distribution -->
                <module>dep</module>
                <module>dist</module>
@@ -502,8 +503,10 @@ limitations under the License.
                                                                </goals>
                                                                <configuration>
                                                                        <target>
-                                                                               <copy todir="${argeo.rpm.stagingRepository}" quiet="true" failonerror="false">
-                                                                                       <fileset dir="${project.build.directory}/rpm" includes="*/RPMS/**/*.rpm" />
+                                                                               <copy todir="${argeo.rpm.stagingRepository}" quiet="true"
+                                                                                       failonerror="false">
+                                                                                       <fileset dir="${project.build.directory}/rpm"
+                                                                                               includes="*/RPMS/**/*.rpm" />
                                                                                        <flattenmapper />
                                                                                </copy>
                                                                        </target>
@@ -528,8 +531,10 @@ limitations under the License.
                                                                </goals>
                                                                <configuration>
                                                                        <target>
-                                                                               <copy todir="${argeo.rpm.stagingRepository}" quiet="true" failonerror="false">
-                                                                                       <fileset dir="${project.build.directory}/rpm" includes="*/RPMS/**/*.rpm" />
+                                                                               <copy todir="${argeo.rpm.stagingRepository}" quiet="true"
+                                                                                       failonerror="false">
+                                                                                       <fileset dir="${project.build.directory}/rpm"
+                                                                                               includes="*/RPMS/**/*.rpm" />
                                                                                        <flattenmapper />
                                                                                </copy>
                                                                        </target>