[maven-release-plugin] copy for tag argeo-commons-2.1.11
authorMathieu Baudier <mbaudier@argeo.org>
Sat, 15 Nov 2014 16:32:28 +0000 (16:32 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Sat, 15 Nov 2014 16:32:28 +0000 (16:32 +0000)
git-svn-id: https://svn.argeo.org/commons/tags/argeo-commons-2.1.11@7476 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

846 files changed:
trunk/.project [new file with mode: 0644]
trunk/base/dep/org.argeo.dep.jackrabbit/pom.xml [new file with mode: 0644]
trunk/base/dep/org.argeo.dep.log4j/pom.xml [new file with mode: 0644]
trunk/base/dep/org.argeo.eclipse.dep.rap/p2.inf [new file with mode: 0644]
trunk/base/dep/org.argeo.eclipse.dep.rap/pom.xml [new file with mode: 0644]
trunk/base/dep/org.argeo.eclipse.dep.rcp/p2.inf [new file with mode: 0644]
trunk/base/dep/org.argeo.eclipse.dep.rcp/pom.xml [new file with mode: 0644]
trunk/base/dep/pom.xml [new file with mode: 0644]
trunk/base/dist/osgi-boot/pom.xml [new file with mode: 0644]
trunk/base/dist/osgi-boot/src/main/rpm/etc/osgiboot/osgi-service-init-functions.sh [new file with mode: 0644]
trunk/base/dist/osgi-boot/src/main/rpm/etc/osgiboot/osgi-service-settings.sh [new file with mode: 0644]
trunk/base/dist/osgi-boot/src/main/rpm/usr/sbin/osgi-service [new file with mode: 0644]
trunk/base/dist/pom.xml [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/.classpath [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/.project [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/build.properties [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/active.gif [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/bundles.gif [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/installed.gif [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/osgi_explorer.gif [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/resolved.gif [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/starting.gif [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/plugin.xml [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/pom.xml [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/OsgiExplorerImages.java [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/OsgiExplorerPerspective.java [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/OsgiExplorerPlugin.java [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/views/BundlesView.java [new file with mode: 0644]
trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/views/ModulesView.java [new file with mode: 0644]
trunk/base/plugins/pom.xml [new file with mode: 0644]
trunk/base/pom.xml [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/.classpath [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/.project [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/build.properties [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/binary.png [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/file.gif [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/folder.gif [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/home.gif [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/node.gif [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/remote_connected.gif [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/remote_disconnected.gif [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/repositories.gif [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/repository_connected.gif [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/repository_disconnected.gif [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/sort.gif [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/workspace_connected.png [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/workspace_disconnected.png [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/pom.xml [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrImages.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrPreferenceStore.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrUiPlugin.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrUiUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/NodeElementComparer.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/NodesWrapper.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/WrappedNode.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/AddFileFolder.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/DeleteNodes.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/OpenGenericJcrQueryEditor.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/Refresh.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/editors/AbstractJcrQueryEditor.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/editors/JcrQueryEditorInput.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/editors/NodeEditorInput.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/ColumnDefinition.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/IListProvider.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/utils/JcrFileProvider.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/utils/JcrItemsComparator.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/utils/NodeViewerComparer.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/utils/SingleSessionFileProvider.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/views/AbstractJcrBrowser.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/resources/org/argeo/eclipse/ui/jcr/messages.properties [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/.classpath [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/.project [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/build.properties [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/pom.xml [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/DownloadServiceHandler.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/FileHandler.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/FileProvider.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/GenericUploadControl.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/ImportToServerWizardPage.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/OpenFile.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/OpenFileService.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/ThreadNLS.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/UploadFileWizardPage.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/.classpath [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/.project [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/build.properties [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/pom.xml [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/DefaultNLS.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/FileHandler.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/FileProvider.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/GenericUploadControl.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/ImportToServerWizardPage.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/OpenFile.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/ThreadNLS.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/UploadFileWizardPage.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/.classpath [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/.project [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/build.properties [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/pom.xml [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/spring/ApplicationContextTracker.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/spring/SpringCommandHandler.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/spring/SpringExtensionFactory.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/AbstractTreeContentProvider.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/ArgeoUiPlugin.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/ColumnViewerComparator.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/EclipseArgeoMonitor.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/EclipseUiUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/Error.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/ErrorFeedback.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/GenericTableComparator.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/TreeParent.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/dialogs/Error.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/dialogs/SingleValue.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/utils/CommandUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/utils/ViewerUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/.classpath [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/.project [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/build.properties [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/pom.xml [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/Activator.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/AdminThread.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/DistributionBundle.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/Launcher.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/OsgiBoot.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/OsgiBootException.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/OsgiBootUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/AntPathMatcher.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/CollectionUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/ObjectUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/PathMatcher.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/StringUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/SystemPropertyUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/jars/test.jar [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/others/subdir/org.argeo.osgi.boot.test.bundle3/META-INF/MANIFEST.MF [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/some/excluded/org.argeo.osgi.boot.test.bundle0/META-INF/MANIFEST.MF [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/some/org.argeo.osgi.boot.test.bundle1/META-INF/MANIFEST.MF [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/some/org.argeo.osgi.boot.test.bundle2/META-INF/MANIFEST.MF [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/test/java/org/argeo/osgi/boot/OsgiBootNoRuntimeTest.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.osgi.boot/src/test/java/org/argeo/osgi/boot/OsgiBootRuntimeTest.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.support.junit/.classpath [new file with mode: 0644]
trunk/base/runtime/org.argeo.support.junit/.project [new file with mode: 0644]
trunk/base/runtime/org.argeo.support.junit/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/base/runtime/org.argeo.support.junit/.settings/org.maven.ide.eclipse.prefs [new file with mode: 0644]
trunk/base/runtime/org.argeo.support.junit/pom.xml [new file with mode: 0644]
trunk/base/runtime/org.argeo.support.junit/src/main/java/org/argeo/support/junit/AbstractSpringTestCase.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/.classpath [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/.project [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/build.properties [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/pom.xml [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/ArgeoException.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/ArgeoLogListener.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/ArgeoLogger.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/ArgeoMonitor.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/OperatingSystem.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/StreamUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/CsvParser.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/CsvParserWithLinesAsMap.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/CsvWriter.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/LocaleCallback.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/LocaleUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/Throughput.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/ThroughputEditor.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/security/DigestUtils.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/security/Keyring.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/security/SimplePrincipal.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/ArrayTabularRow.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/CsvTabularWriter.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularColumn.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularContent.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularRow.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularRowIterator.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularWriter.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvParserEncodingTestCase.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvParserParseFileTest.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvParserTestCase.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvParserWithQuotedSeparatorTest.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvWriterTestCase.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/ThroughputTest.java [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/test/resources/org/argeo/util/ReferenceFile.csv [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/test/resources/org/argeo/util/TestParse-ISO.csv [new file with mode: 0644]
trunk/base/runtime/org.argeo.util/src/test/resources/org/argeo/util/TestParse-UTF-8.csv [new file with mode: 0644]
trunk/base/runtime/pom.xml [new file with mode: 0644]
trunk/demo/.project [new file with mode: 0644]
trunk/demo/argeo_node_cli.properties [new file with mode: 0644]
trunk/demo/argeo_node_rcp.properties [new file with mode: 0644]
trunk/demo/argeo_node_rcp_remote.properties [new file with mode: 0644]
trunk/demo/argeo_node_web.properties [new file with mode: 0644]
trunk/demo/log4j.properties [new file with mode: 0644]
trunk/demo/ssl/ca.crt [new file with mode: 0644]
trunk/demo/ssl/ca.key [new file with mode: 0644]
trunk/demo/ssl/demo@demo.crt [new file with mode: 0644]
trunk/demo/ssl/demo@demo.csr [new file with mode: 0644]
trunk/demo/ssl/demo@demo.key [new file with mode: 0644]
trunk/demo/ssl/demo@demo.p12 [new file with mode: 0644]
trunk/demo/ssl/root@demo.crt [new file with mode: 0644]
trunk/demo/ssl/root@demo.csr [new file with mode: 0644]
trunk/demo/ssl/root@demo.key [new file with mode: 0644]
trunk/demo/ssl/root@demo.p12 [new file with mode: 0644]
trunk/demo/ssl/server.ks [new file with mode: 0644]
trunk/demo/ssl/server.ts [new file with mode: 0644]
trunk/demo/ssl/ssl.txt [new file with mode: 0644]
trunk/demo/ssl/tomcat.crt [new file with mode: 0644]
trunk/demo/ssl/tomcat.csr [new file with mode: 0644]
trunk/doc/files/etc/yum.repos.d/argeo-staging.repo [new file with mode: 0644]
trunk/doc/pom.xml [new file with mode: 0644]
trunk/doc/reference/commons-gettingStarted.xml [new file with mode: 0644]
trunk/doc/reference/css/style.css [new file with mode: 0644]
trunk/doc/site/apt/index.apt [new file with mode: 0644]
trunk/doc/site/site.xml [new file with mode: 0644]
trunk/license-apache2-header.txt [new file with mode: 0644]
trunk/pom.xml [new file with mode: 0644]
trunk/security/dep/org.argeo.security.dep.node.eclipse/p2.inf [new file with mode: 0644]
trunk/security/dep/org.argeo.security.dep.node.eclipse/pom.xml [new file with mode: 0644]
trunk/security/dep/org.argeo.security.dep.node.rap/p2.inf [new file with mode: 0644]
trunk/security/dep/org.argeo.security.dep.node.rap/pom.xml [new file with mode: 0644]
trunk/security/dep/org.argeo.security.dep.node.rap/src/assembly/dist.xml [new file with mode: 0644]
trunk/security/dep/org.argeo.security.dep.node.rcp/p2.inf [new file with mode: 0644]
trunk/security/dep/org.argeo.security.dep.node.rcp/pom.xml [new file with mode: 0644]
trunk/security/dep/org.argeo.security.dep.node.rcp/src/assembly/linux.x86.xml [new file with mode: 0644]
trunk/security/dep/org.argeo.security.dep.node.rcp/src/assembly/linux.x86_64.xml [new file with mode: 0644]
trunk/security/dep/org.argeo.security.dep.node.rcp/src/assembly/win32.x86.xml [new file with mode: 0644]
trunk/security/dep/org.argeo.security.dep.node/p2.inf [new file with mode: 0644]
trunk/security/dep/org.argeo.security.dep.node/pom.xml [new file with mode: 0644]
trunk/security/dep/pom.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.auth.ldap/.project [new file with mode: 0644]
trunk/security/modules/org.argeo.security.auth.ldap/META-INF/spring/security-ldap-jcr.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.auth.ldap/META-INF/spring/security-ldap-osgi.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.auth.ldap/META-INF/spring/security-ldap-services.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.auth.ldap/META-INF/spring/security-ldap.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.auth.ldap/build.properties [new file with mode: 0644]
trunk/security/modules/org.argeo.security.auth.ldap/ldap.properties [new file with mode: 0644]
trunk/security/modules/org.argeo.security.auth.ldap/pom.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.cli/.project [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.cli/META-INF/spring/security-cli-osgi.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.cli/META-INF/spring/security-cli.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.cli/pom.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.cli/security.properties [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.jackrabbit/.project [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/repofactory.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/security-jcr-osgi.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/security-jcr-services.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.jackrabbit/build.properties [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.jackrabbit/pom.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.jackrabbit/security.properties [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.ldap/.project [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-jcr.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-osgi.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-services.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.ldap/build.properties [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.ldap/ldap.properties [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.ldap/pom.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.os/.project [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.os/META-INF/spring/security-os-osgi.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.os/META-INF/spring/security-os.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.os/build.properties [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.os/pom.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.dao.os/security.properties [new file with mode: 0644]
trunk/security/modules/org.argeo.security.webapp/.project [new file with mode: 0644]
trunk/security/modules/org.argeo.security.webapp/WEB-INF/applicationContext.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.webapp/WEB-INF/osgi.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.webapp/WEB-INF/security-servlet.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.webapp/WEB-INF/security.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.webapp/WEB-INF/web.xml [new file with mode: 0644]
trunk/security/modules/org.argeo.security.webapp/pom.xml [new file with mode: 0644]
trunk/security/modules/pom.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.equinox/.classpath [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.equinox/.project [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.equinox/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.equinox/META-INF/spring/loginModules.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.equinox/META-INF/spring/osgi.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.equinox/build.properties [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.equinox/plugin.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.equinox/pom.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.equinox/security.properties [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/OsSpringLoginModule.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/SpringLoginModule.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/.classpath [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/.project [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/commands.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/common.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/editors.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/osgi.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/views.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/build.properties [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/icons/add.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/icons/batch.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/icons/clear.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/icons/refresh.png [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/icons/remove.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/icons/role.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/icons/save.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/icons/save_security.png [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/icons/save_security_disabled.png [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/icons/security.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/icons/sync.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/icons/user.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/icons/users.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/plugin.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/pom.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/security-admin.properties [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminImages.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminPerspective.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminPlugin.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/UserTableComposite.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/AddRole.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/DeleteRole.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/DeleteUser.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/NewUser.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/OpenArgeoUserEditor.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/RefreshRoles.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/RefreshUsersList.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/SaveArgeoUser.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/UserBatchUpdate.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/ArgeoUserEditor.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/ArgeoUserEditorInput.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/DefaultUserMainPage.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/UserRolesPage.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/RolesView.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/UsersView.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/MainUserInfoWizardPage.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/NewUserWizard.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/UserBatchUpdateWizard.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/.classpath [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/.project [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/META-INF/jaas_default.txt [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/META-INF/spring/commands.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/META-INF/spring/osgi.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/branding/afterLogout.html [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/branding/empty.html [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/branding/favicon.ico [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/branding/login.html [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/branding/public.html [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/build.properties [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/icons/closeAll.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/icons/exit.png [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/icons/home.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/icons/main.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/icons/password.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/icons/preferences.png [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/plugin.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/pom.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/AnonymousEntryPoint.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/LogoutEntryPoint.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/NullEntryPoint.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/RapActionBarAdvisor.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/RapWindowAdvisor.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/SecureEntryPoint.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/SecureRapActivator.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/commands/UserMenu.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/.classpath [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/.project [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/META-INF/jaas_default.txt [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/build.properties [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_about.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_launcher.icns [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_launcher.ico [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_launcher.xpm [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_window_16.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_window_32.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/plugin.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/plugin_customization.ini [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/pom.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/AbstractSecureApplication.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureActionBarAdvisor.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureApplicationActivator.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureRcp.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureWorkbenchAdvisor.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureWorkbenchWindowAdvisor.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/.classpath [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/.project [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/META-INF/spring/commands.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/META-INF/spring/keyring.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/META-INF/spring/osgi.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/META-INF/spring/views.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/build.properties [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/icons/adminLog.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/icons/home.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/icons/log.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/icons/maintenance.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/icons/password.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/icons/user.gif [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/plugin.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/pom.xml [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/securityui.properties [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/MaintenancePerspective.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/PrivilegedJob.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/RolesSourceProvider.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/SecurityUiPlugin.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/UserHomePerspective.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/commands/OpenChangePasswordDialog.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/commands/OpenHomePerspective.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/dialogs/AbstractLoginDialog.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/dialogs/ChangePasswordDialog.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/dialogs/DefaultLoginDialog.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/internal/CurrentUser.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/views/AdminLogView.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/views/LogContentProvider.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/views/LogView.java [new file with mode: 0644]
trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/views/UserProfile.java [new file with mode: 0644]
trunk/security/plugins/pom.xml [new file with mode: 0644]
trunk/security/pom.xml [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.activemq/.classpath [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.activemq/.project [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.activemq/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.activemq/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.activemq/build.properties [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.activemq/pom.xml [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.activemq/src/main/java/org/argeo/security/activemq/ActiveMqSecurityBrokerPlugin.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.activemq/src/main/java/org/argeo/security/activemq/ActiveMqSpringSecurityContext.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.activemq/src/main/java/org/argeo/security/activemq/SecuredActiveMqConnectionFactory.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.activemq/src/main/java/org/argeo/security/activemq/UserPasswordDialog.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.activemq/src/main/resources/org/argeo/security/activemq/osLogin.conf [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/.classpath [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/.project [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/META-INF/spring/logger.xml [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/META-INF/spring/osgi.xml [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/build.properties [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/pom.xml [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/NodeAuthenticationToken.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/OsAuthenticationToken.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SecurityUtils.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SystemAuthentication.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SystemExecutionService.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/UserAdminService.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/AbstractSystemExecution.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/AsyncSystemTaskExecutor.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/AuthenticationProvidersRegister.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/ConsoleCallbackHandler.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/InternalAuthentication.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/KeyBasedSystemExecutionService.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/MatchingAuthenticationProvider.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/OsAuthenticationProvider.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/OsgiModuleLabel.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/SimpleRoleRegistration.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/AbstractKeyring.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/CryptoKeyring.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/KeyringLoginModule.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PBEKeySpecCallback.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PasswordBasedEncryption.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrKeyring.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrSecurityModel.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrUserDetails.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrAuthenticationProvider.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrUserAdminService.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrAuthenticationProvider.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrRepositoryWrapper.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SecureThreadBoundSession.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SimpleJcrSecurityModel.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/log4j/SecureLogger.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/PasswordSandbox.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/crypto/PasswordBasedEncryptionTest.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/test/resources/log4j.properties [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.core/src/test/resources/org/argeo/security/json/gandalf2.json [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.jackrabbit/.classpath [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.jackrabbit/.project [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.jackrabbit/build.properties [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.jackrabbit/pom.xml [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoAccessManager.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoLoginModule.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoSecurityManager.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoSystemPrincipal.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/GrantedAuthorityPrincipal.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/JackrabbitSecurityModel.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.ldap/.classpath [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.ldap/.project [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.ldap/build.properties [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.ldap/pom.xml [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/ArgeoLdapShaPasswordEncoder.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/ArgeoLdapUserDetailsManager.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/ArgeoUserAdminDaoLdap.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrLdapSynchronizer.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.mvc/.classpath [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.mvc/.project [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.mvc/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.mvc/.settings/org.maven.ide.eclipse.prefs [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.mvc/build.properties [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.mvc/pom.xml [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.mvc/src/main/java/org/argeo/security/mvc/ArgeoRememberMeServices.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.mvc/src/main/java/org/argeo/security/mvc/ArgeoUserInterceptor.java [new file with mode: 0644]
trunk/security/runtime/org.argeo.security.mvc/src/main/java/org/argeo/security/mvc/UsersRolesController.java [new file with mode: 0644]
trunk/security/runtime/pom.xml [new file with mode: 0644]
trunk/server/dep/org.argeo.server.dep.activemq/pom.xml [new file with mode: 0644]
trunk/server/dep/org.argeo.server.dep.ads/pom.xml [new file with mode: 0644]
trunk/server/dep/org.argeo.server.dep.hibernate/pom.xml [new file with mode: 0644]
trunk/server/dep/org.argeo.server.dep.jackrabbit.server/pom.xml [new file with mode: 0644]
trunk/server/dep/org.argeo.server.dep.tomcat/pom.xml [new file with mode: 0644]
trunk/server/dep/pom.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.ext.bsf/pom.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.ext.jackrabbit.sybase/.project [new file with mode: 0644]
trunk/server/modules/org.argeo.ext.jackrabbit.sybase/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/server/modules/org.argeo.ext.jackrabbit.sybase/build.properties [new file with mode: 0644]
trunk/server/modules/org.argeo.ext.jackrabbit.sybase/org/apache/jackrabbit/core/fs/db/sybase.ddl [new file with mode: 0644]
trunk/server/modules/org.argeo.ext.jackrabbit.sybase/org/apache/jackrabbit/core/journal/sybase.ddl [new file with mode: 0644]
trunk/server/modules/org.argeo.ext.jackrabbit.sybase/org/apache/jackrabbit/core/persistence/bundle/sybase.ddl [new file with mode: 0644]
trunk/server/modules/org.argeo.ext.jackrabbit.sybase/org/apache/jackrabbit/core/persistence/db/sybase.ddl [new file with mode: 0644]
trunk/server/modules/org.argeo.ext.jackrabbit.sybase/pom.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.ext.jdbm/.project [new file with mode: 0644]
trunk/server/modules/org.argeo.ext.jdbm/build.properties [new file with mode: 0644]
trunk/server/modules/org.argeo.ext.jdbm/pom.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.jackrabbit.webapp/.project [new file with mode: 0644]
trunk/server/modules/org.argeo.jackrabbit.webapp/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/applicationContext.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/osgi.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/remoting-servlet.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security-filters.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/web.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/webdav-config.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/webdav-servlet.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.jackrabbit.webapp/build.properties [new file with mode: 0644]
trunk/server/modules/org.argeo.jackrabbit.webapp/jackrabbit-webapp.properties [new file with mode: 0644]
trunk/server/modules/org.argeo.jackrabbit.webapp/pom.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repo.jackrabbit/.project [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repo.jackrabbit/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo-osgi.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repo.jackrabbit/build.properties [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repo.jackrabbit/noderepo.properties [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repo.jackrabbit/pom.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repo.jackrabbit/repository-h2.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repo.jackrabbit/repository-memory.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repo.jackrabbit/repository-mysql.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repo.jackrabbit/repository-postgresql.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repofactory.jackrabbit/.project [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repofactory.jackrabbit/META-INF/spring/repofactory-osgi.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repofactory.jackrabbit/META-INF/spring/repofactory.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.node.repofactory.jackrabbit/pom.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.activemq.broker/.project [new file with mode: 0644]
trunk/server/modules/org.argeo.server.activemq.broker/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/server/modules/org.argeo.server.activemq.broker/META-INF/spring/activemq-broker.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.activemq.broker/activemq.properties [new file with mode: 0644]
trunk/server/modules/org.argeo.server.activemq.broker/build.properties [new file with mode: 0644]
trunk/server/modules/org.argeo.server.activemq.broker/pom.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.ads.server/.project [new file with mode: 0644]
trunk/server/modules/org.argeo.server.ads.server/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/server/modules/org.argeo.server.ads.server/META-INF/spring/ads.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.ads.server/META-INF/spring/server.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.ads.server/ads.properties [new file with mode: 0644]
trunk/server/modules/org.argeo.server.ads.server/build.properties [new file with mode: 0644]
trunk/server/modules/org.argeo.server.ads.server/init.ldif [new file with mode: 0644]
trunk/server/modules/org.argeo.server.ads.server/pom.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.catalina/conf/catalina.policy [new file with mode: 0644]
trunk/server/modules/org.argeo.server.catalina/conf/catalina.properties [new file with mode: 0644]
trunk/server/modules/org.argeo.server.catalina/conf/context.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.catalina/conf/web.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.catalina/pom.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.jdbc/.project [new file with mode: 0644]
trunk/server/modules/org.argeo.server.jdbc/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/server/modules/org.argeo.server.jdbc/build.properties [new file with mode: 0644]
trunk/server/modules/org.argeo.server.jdbc/pom.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.rap.webapp/.project [new file with mode: 0644]
trunk/server/modules/org.argeo.server.rap.webapp/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/server/modules/org.argeo.server.rap.webapp/META-INF/context-template.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/applicationContext.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/osgi.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/security-filters.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/service-servlet.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/web.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.rap.webapp/build.properties [new file with mode: 0644]
trunk/server/modules/org.argeo.server.rap.webapp/pom.xml [new file with mode: 0644]
trunk/server/modules/org.argeo.server.rap.webapp/rap-webapp.properties [new file with mode: 0644]
trunk/server/modules/pom.xml [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/.classpath [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/.project [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/commands.xml [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/editors.xml [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/jcr.xml [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/osgi.xml [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/views.xml [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/build.properties [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/add.gif [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addFolder.gif [new file with mode: 0755]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addPrivileges.png [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addRepo.gif [new file with mode: 0755]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addWorkspace.png [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/browser.gif [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/dumpNode.gif [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/getSize.gif [new file with mode: 0755]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/import_fs.png [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/nodes.gif [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/query.png [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/refresh.png [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/remove.gif [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/repositories.gif [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/sort.gif [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/plugin.properties [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/plugin.xml [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/pom.xml [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerConstants.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPerspective.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPlugin.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerView.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeContentProvider.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeLabelProvider.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/PropertiesContentProvider.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/AddFolderNode.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/AddPrivileges.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/AddRemoteRepository.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/CreateWorkspace.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/DeleteNodes.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/DumpNode.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/EditNode.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/GetNodeSize.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/ImportFileSystem.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/OpenGenericNodeEditor.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/Refresh.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/RemoveRemoteRepository.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/SortChildNodes.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/dialogs/ChooseNameDialog.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/ChildNodesPage.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/EmptyNodePage.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericJcrQueryEditor.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditor.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditorInput.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodePage.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericPropertyPage.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/NodeRightsManagementPage.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/NodeVersionHistoryPage.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/StringNodeEditorInput.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/MaintainedRepositoryElem.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/RemoteRepositoryElem.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/RepositoriesElem.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/RepositoryElem.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/SingleJcrNodeElem.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/WorkspaceElem.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/providers/FullVersioningTreeContentProvider.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/providers/PropertyLabelProvider.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/providers/SingleNodeAsTreeContentProvider.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/providers/VersionLabelProvider.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/GenericNodeDoubleClickListener.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/JcrUiUtils.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/TreeObjectsComparator.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/views/GenericJcrBrowser.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/wizards/ChangeRightsWizard.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/wizards/ChooseRightsPage.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/wizards/ImportFileSystemWizard.java [new file with mode: 0644]
trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/resources/org/argeo/jcr/ui/explorer/messages.properties [new file with mode: 0644]
trunk/server/plugins/pom.xml [new file with mode: 0644]
trunk/server/pom.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.ads/.classpath [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.ads/.project [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.ads/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.ads/.settings/org.maven.ide.eclipse.prefs [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.ads/build.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.ads/pom.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.ads/src/main/java/org/argeo/server/ads/AdsContainer.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.catalina.start/.classpath [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.catalina.start/.project [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.catalina.start/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.catalina.start/build.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.catalina.start/pom.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.catalina.start/src/main/java/org/argeo/catalina/start/CatalinaActivator.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.catalina.start/src/main/java/org/springframework/osgi/web/tomcat/internal/Activator.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.catalina.start/src/main/java/org/springframework/osgi/web/tomcat/internal/OsgiCatalina.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.catalina.start/src/main/resources/conf/default-server-ssl.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.catalina.start/src/main/resources/conf/default-server.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.catalina.start/tomcat.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/.classpath [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/.project [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/build.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/pom.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/BooleanAnswer.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/Deserializer.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/DeserializingEditor.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/PrimitiveAnswer.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/Serializer.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/ServerAnswer.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/ServerSerializer.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/AbstractAtomicBackup.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/AtomicBackup.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/BackupContext.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/BackupFileSystemManager.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/BackupPurge.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/BackupUtils.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/MySqlBackup.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/OpenLdapBackup.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/OsCallBackup.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/PostgreSqlBackup.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/SimpleBackupContext.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/SimpleBackupPurge.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/SvnBackup.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/SystemBackup.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/AbstractMemoryDaoSupport.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/AbstractTabularDaoSupport.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/LightDaoAware.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/LightDaoPropertyEditor.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/LightDaoSupport.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/DefaultHandlerExceptionResolver.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/EmptyViewController.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/MvcConstants.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/SerializingView.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/SerializingViewResolver.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.hibernate/.classpath [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.hibernate/.project [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.hibernate/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.hibernate/.settings/org.maven.ide.eclipse.prefs [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.hibernate/build.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.hibernate/pom.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.hibernate/src/main/java/org/argeo/server/hibernate/HibernateLightDaoSync.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.hibernate/src/main/java/org/argeo/server/hibernate/LightDaoInterceptor.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/.classpath [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/.project [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/build.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/pom.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitAuthorizations.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitContainer.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitDataModelMigration.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitWrapper.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/OsgiJackrabbitRepositoryFactory.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/AbstractJackrabbitHandlerMapping.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/ExtendedDispatcherServlet.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/IOHandlerWrapper.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/IOManagerBean.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/JcrRemotingHandlerMapping.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/JcrRemotingServlet.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/OpenInViewSessionProvider.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/ScopedSessionProvider.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleSessionProvider.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleWebdavHandlerMapping.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleWebdavServlet.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/resources/org/argeo/jackrabbit/repository-fs.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/resources/org/argeo/jackrabbit/repository-h2.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/resources/org/argeo/jackrabbit/repository-memory.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jackrabbit/src/main/wikitext/design.mediawiki [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr.mvc/.classpath [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr.mvc/.project [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr.mvc/build.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr.mvc/pom.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/MultipleRepositoryHandlerMapping.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/ResourceProxyServlet.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/.classpath [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/.project [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/build.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/pom.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoJcrConstants.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoJcrUtils.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoNames.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoTypes.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/CollectionNodeIterator.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/DefaultJcrListener.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/DefaultRepositoryFactory.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/DefaultRepositoryRegister.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrCallback.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrRepositoryWrapper.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrResourceAdapter.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUrlStreamHandler.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/MaintainedRepository.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/NodeMapper.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/NodeMapperProvider.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/PropertyDiff.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/RepositoryRegister.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ThreadBoundJcrSessionFactory.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/UserJcrUtils.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/VersionDiff.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/AbstractUrlProxy.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/ResourceProxy.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/security/JcrAuthorizations.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/spring/BeanNodeMapper.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/spring/ThreadBoundSession.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/tabular/JcrTabularRowIterator.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/tabular/JcrTabularWriter.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/unit/AbstractJcrTestCase.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/main/resources/org/argeo/jcr/argeo.cnd [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/AbstractInternalJackrabbitTestCase.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/CollectionsObject.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/MapperTest.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/OtherObject.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/SimpleObject.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/tabular/JcrTabularTest.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/server/jcr/JcrResourceAdapterTest.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/resources/log4j.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy00.xls [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy01.xls [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy02.xls [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy03.xls [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/repository-h2.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/repository-memory.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.json/.classpath [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.json/.project [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.json/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.json/.settings/org.maven.ide.eclipse.prefs [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.json/build.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.json/pom.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/GenericJsonDeserializer.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/JsonObjectFactory.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/JsonObjectFactoryImpl.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/JsonServerMapper.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/JsonServerSerializer.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/.classpath [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/.project [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/.settings/org.maven.ide.eclipse.prefs [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/build.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/pom.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/src/main/java/org/argeo/server/jxl/dao/JxlDaoSupport.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/CollectionsObject.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/JxlDaoSupportTest.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/OtherObject.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/SimpleObject.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/SimpleObjectEditor.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/src/test/resources/dao/simple.xls [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.jxl/src/test/resources/log4j.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.modeshape/.classpath [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.modeshape/.project [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.modeshape/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.modeshape/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.modeshape/build.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.modeshape/pom.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.modeshape/src/main/java/org/argeo/modeshape/FileSystemRepository.java [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.webextender/.classpath [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.webextender/.project [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.webextender/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.webextender/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.webextender/META-INF/spring/extender/extender.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.webextender/META-INF/spring/extender/webextender.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.webextender/build.properties [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.webextender/pom.xml [new file with mode: 0644]
trunk/server/runtime/org.argeo.server.webextender/src/main/java/org/argeo/server/webextender/TomcatDeployer.java [new file with mode: 0644]
trunk/server/runtime/pom.xml [new file with mode: 0644]

diff --git a/trunk/.project b/trunk/.project
new file mode 100644 (file)
index 0000000..e2ee173
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.commons</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+       </buildSpec>
+       <natures>
+       </natures>
+</projectDescription>
diff --git a/trunk/base/dep/org.argeo.dep.jackrabbit/pom.xml b/trunk/base/dep/org.argeo.dep.jackrabbit/pom.xml
new file mode 100644 (file)
index 0000000..efba9b1
--- /dev/null
@@ -0,0 +1,110 @@
+<?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.base</groupId>
+               <version>2.1.11</version>
+               <artifactId>dep</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.dep.jackrabbit</artifactId>
+       <packaging>pom</packaging>
+       <name>Commons Minimal Jackrabbit</name>
+       <dependencies>
+               <!-- Jackrabbit -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.jackrabbit</artifactId>
+               </dependency>
+
+               <!-- Database -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.h2</artifactId>
+               </dependency>
+
+               <!-- Transitive dependencies -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.jcr</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.lucene</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.log4j</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+               </dependency>
+
+               <!-- Commons -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.collections</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.io</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.httpclient</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.fileupload</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.compress</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.pool</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.dbcp</artifactId>
+               </dependency>
+
+               <!-- Misc -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>edu.oswego.cs.dl.util.concurrent</artifactId>
+               </dependency>
+
+               <!-- For webdav -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.servlet</artifactId>
+               </dependency>
+
+               <!-- Tika -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.tika</artifactId>
+               </dependency>
+               <!-- Required for OSGi completion -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.dom4j</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.jdom</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.objectweb.asm</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.jaxen</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.xmlpull</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/base/dep/org.argeo.dep.log4j/pom.xml b/trunk/base/dep/org.argeo.dep.log4j/pom.xml
new file mode 100644 (file)
index 0000000..56acd09
--- /dev/null
@@ -0,0 +1,55 @@
+<?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.base</groupId>
+               <version>2.1.11</version>
+               <artifactId>dep</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.dep.log4j</artifactId>
+       <packaging>pom</packaging>
+       <name>Commons Apache Log4j (SLF4J) Dependencies</name>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.log4j</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.log4j</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.api</artifactId>
+               </dependency>
+               <!-- JEE dependencies (for 1.2.16 in SpringSOurce repo)
+               <dependency>
+                       <groupId>org.apache.geronimo.specs</groupId>
+                       <artifactId>com.springsource.javax.management.j2ee</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>javax.ejb</groupId>
+                       <artifactId>com.springsource.javax.ejb</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>javax.transaction</groupId>
+                       <artifactId>com.springsource.javax.transaction</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>javax.xml.rpc</groupId>
+                       <artifactId>com.springsource.javax.xml.rpc</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>javax.xml.soap</groupId>
+                       <artifactId>com.springsource.javax.xml.soap</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>javax.activation</groupId>
+                       <artifactId>com.springsource.javax.activation</artifactId>
+               </dependency> -->
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/base/dep/org.argeo.eclipse.dep.rap/p2.inf b/trunk/base/dep/org.argeo.eclipse.dep.rap/p2.inf
new file mode 100644 (file)
index 0000000..0423aa5
--- /dev/null
@@ -0,0 +1,2 @@
+properties.1.name=org.eclipse.equinox.p2.type.category
+properties.1.value=true
\ No newline at end of file
diff --git a/trunk/base/dep/org.argeo.eclipse.dep.rap/pom.xml b/trunk/base/dep/org.argeo.eclipse.dep.rap/pom.xml
new file mode 100644 (file)
index 0000000..5df989f
--- /dev/null
@@ -0,0 +1,238 @@
+<?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.base</groupId>
+               <artifactId>dep</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.eclipse.dep.rap</artifactId>
+       <name>Commons Eclipse RAP Dependencies</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.argeo.maven.plugins</groupId>
+                               <artifactId>maven-argeo-osgi-plugin</artifactId>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- <dependency> -->
+               <!-- <groupId>org.argeo.tp</groupId> -->
+               <!-- <artifactId>org.eclipse.osgi</artifactId> -->
+               <!-- </dependency> -->
+
+               <dependency>
+                       <groupId>org.argeo.tp.rap.addons</groupId>
+                       <artifactId>binaries</artifactId>
+                       <version>${version.rap.addons}</version>
+                       <type>pom</type>
+                       <exclusions>
+                               <exclusion>
+                                       <!-- TODO remove OSGi runtime from addons -->
+                                       <groupId>org.argeo.tp.rap.addons</groupId>
+                                       <artifactId>org.eclipse.osgi</artifactId>
+                               </exclusion>
+                       </exclusions>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.argeo.tp.rap.platform</groupId>
+                       <artifactId>binaries</artifactId>
+                       <version>${version.rap}</version>
+                       <type>pom</type>
+                       <exclusions>
+                               <!-- <exclusion> -->
+                               <!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
+                               <!-- <artifactId>javax.servlet</artifactId> -->
+                               <!-- </exclusion> -->
+
+                               <!-- Jetty -->
+                               <!-- <exclusion> -->
+                               <!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
+                               <!-- <artifactId>org.eclipse.equinox.http.jetty</artifactId> -->
+                               <!-- </exclusion> -->
+                               <!-- <exclusion> -->
+                               <!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
+                               <!-- <artifactId>org.mortbay.jetty.server</artifactId> -->
+                               <!-- </exclusion> -->
+                               <!-- <exclusion> -->
+                               <!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
+                               <!-- <artifactId>org.mortbay.jetty.util</artifactId> -->
+                               <!-- </exclusion> -->
+
+                               <!-- <exclusion> -->
+                               <!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
+                               <!-- <artifactId>org.eclipse.jetty.security</artifactId> -->
+                               <!-- </exclusion> -->
+                               <!-- <exclusion> -->
+                               <!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
+                               <!-- <artifactId>org.eclipse.jetty.http</artifactId> -->
+                               <!-- </exclusion> -->
+                               <!-- <exclusion> -->
+                               <!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
+                               <!-- <artifactId>org.eclipse.jetty.server</artifactId> -->
+                               <!-- </exclusion> -->
+                               <!-- <exclusion> -->
+                               <!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
+                               <!-- <artifactId>org.eclipse.jetty.continuation</artifactId> -->
+                               <!-- </exclusion> -->
+                               <!-- <exclusion> -->
+                               <!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
+                               <!-- <artifactId>org.eclipse.jetty.util</artifactId> -->
+                               <!-- </exclusion> -->
+                               <!-- <exclusion> -->
+                               <!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
+                               <!-- <artifactId>org.eclipse.jetty.io</artifactId> -->
+                               <!-- </exclusion> -->
+                               <!-- <exclusion> -->
+                               <!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
+                               <!-- <artifactId>org.eclipse.jetty.servlet</artifactId> -->
+                               <!-- </exclusion> -->
+
+                               <!-- Demo -->
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rap.platform</groupId>
+                                       <artifactId>org.eclipse.rap.demo</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rap.platform</groupId>
+                                       <artifactId>org.eclipse.rap.demo.databinding</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rap.platform</groupId>
+                                       <artifactId>org.eclipse.rap.design.example</artifactId>
+                               </exclusion>
+
+                               <!-- Troublesome bundles -->
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rap.platform</groupId>
+                                       <artifactId>org.eclipse.jdt.junit.runtime</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rap.platform</groupId>
+                                       <artifactId>org.eclipse.rap.junit.runtime</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rap.platform</groupId>
+                                       <artifactId>org.eclipse.rap.rwt.testfixture</artifactId>
+                               </exclusion>
+                       </exclusions>
+               </dependency>
+
+               <!-- Dependency to enable file upload. Should be included directly in coming 
+                       rap distribution -->
+               <!-- <dependency> -->
+               <!-- <groupId>org.argeo.tp.rap</groupId> -->
+               <!-- <artifactId>org.eclipse.rwt.widgets.upload</artifactId> -->
+               <!-- </dependency> -->
+               <!-- Needed by the bundle above -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.io</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.fileupload</artifactId>
+               </dependency>
+
+               <!-- RAP Specific Dependencies -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.servlet</artifactId>
+               </dependency>
+
+               <!-- Security -->
+               <dependency>
+                       <groupId>org.argeo.tp.rap</groupId>
+                       <artifactId>org.eclipse.equinox.security</artifactId>
+               </dependency>
+
+               <!-- 2014-07-31 mbaudier : Removed, since it feels wrong to put an Argeo 
+                       dependency here -->
+               <!-- <dependency> -->
+               <!-- <groupId>org.argeo.commons.base</groupId> -->
+               <!-- <artifactId>org.argeo.osgi.boot</artifactId> -->
+               <!-- <version>2.1.8-SNAPSHOT</version> -->
+               <!-- </dependency> -->
+       </dependencies>
+       <profiles>
+               <profile>
+                       <id>check-osgi</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.argeo.maven.plugins</groupId>
+                                               <artifactId>maven-argeo-osgi-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>check-osgi</id>
+                                                               <phase>test</phase>
+                                                               <goals>
+                                                                       <goal>equinox</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <onlyCheck>true</onlyCheck>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+               </profile>
+               <profile>
+                       <id>rpmbuild-tp</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.codehaus.mojo</groupId>
+                                               <artifactId>rpm-maven-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>rpm-rap</id>
+                                                               <phase>package</phase>
+                                                               <goals>
+                                                                       <goal>rpm</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <name>eclipse-rap</name>
+                                                                       <projversion>${version.rap}</projversion>
+                                                                       <mappings>
+                                                                               <mapping>
+                                                                                       <directory>/usr/share/eclipse-rap</directory>
+                                                                                       <username>root</username>
+                                                                                       <groupname>root</groupname>
+                                                                                       <filemode>644</filemode>
+                                                                                       <directoryIncluded>false</directoryIncluded>
+                                                                                       <dependency />
+                                                                               </mapping>
+                                                                       </mappings>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                                       <plugin>
+                                               <artifactId>maven-antrun-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <phase>install</phase>
+                                                               <goals>
+                                                                       <goal>run</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <target>
+                                                                               <copy todir="${rpm.stagingRepository}" verbose="true">
+                                                                                       <fileset dir="${project.build.directory}/rpm" includes="*/RPMS/**/*.rpm" />
+                                                                                       <flattenmapper />
+                                                                               </copy>
+                                                                       </target>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+               </profile>
+       </profiles>
+</project>
\ No newline at end of file
diff --git a/trunk/base/dep/org.argeo.eclipse.dep.rcp/p2.inf b/trunk/base/dep/org.argeo.eclipse.dep.rcp/p2.inf
new file mode 100644 (file)
index 0000000..0423aa5
--- /dev/null
@@ -0,0 +1,2 @@
+properties.1.name=org.eclipse.equinox.p2.type.category
+properties.1.value=true
\ No newline at end of file
diff --git a/trunk/base/dep/org.argeo.eclipse.dep.rcp/pom.xml b/trunk/base/dep/org.argeo.eclipse.dep.rcp/pom.xml
new file mode 100644 (file)
index 0000000..0149471
--- /dev/null
@@ -0,0 +1,261 @@
+<?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.base</groupId>
+               <artifactId>dep</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.eclipse.dep.rcp</artifactId>
+       <name>Commons Eclipse RCP Dependencies</name>
+       <description><![CDATA[
+com.ibm.icu
+org.eclipse.core.commands
+org.eclipse.core.contenttype
+org.eclipse.core.databinding
+org.eclipse.core.databinding.observable
+org.eclipse.core.databinding.property
+org.eclipse.core.expressions
+org.eclipse.core.jobs
+org.eclipse.core.runtime
+org.eclipse.equinox.app
+org.eclipse.equinox.common
+org.eclipse.equinox.launcher
+org.eclipse.equinox.preferences
+org.eclipse.equinox.registry
+org.eclipse.equinox.security
+org.eclipse.help
+org.eclipse.jface
+org.eclipse.jface.databinding
+org.eclipse.osgi
+org.eclipse.swt
+org.eclipse.swt.gtk.linux.x86
+org.eclipse.swt.gtk.linux.x86_64
+org.eclipse.swt.win32.win32.x86
+org.eclipse.ui
+org.eclipse.ui.forms
+org.eclipse.ui.workbench       
+
+/usr/lib64/eclipse/plugins;\
+in=com.ibm.icu*.jar;\
+in=org.eclipse.core*.jar;\
+in=org.eclipse.equinox*.jar;\
+in=org.eclipse.jface*.jar;\
+in=org.eclipse.osgi*.jar;\
+in=org.eclipse.swt*.jar;\
+in=org.eclipse.ui*.jar;\
+in=org.eclipse.help_*.jar;\
+ex=org.eclipse.osgi.jar;\
+ex=org.eclipse.equinox.p2*.jar;\
+ex=org.eclipse.equinox.simpleconfigurator*.jar;\
+ex=*.source_*.jar,\
+
+       ]]></description>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.argeo.maven.plugins</groupId>
+                               <artifactId>maven-argeo-osgi-plugin</artifactId>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.eclipse.osgi</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                       <artifactId>binaries</artifactId>
+                       <version>${version.argeo-distribution}</version>
+                       <type>pom</type>
+                       <exclusions>
+                               <!-- Deprecated update configurator has side effect in PDE -->
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.update.configurator</artifactId>
+                               </exclusion>
+                               <!-- SWT -->
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.win32.win32.x86_64</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.carbon.macosx</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.cocoa.macosx.x86_64</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.gtk.solaris.sparc</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.gtk.solaris.x86</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.motif.solaris.sparc</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.gtk.linux.s390</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.gtk.linux.s390x</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.gtk.linux.ppc64</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.motif.linux.x86</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.gtk.linux.ppc</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.photon.qnx.x86</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.motif.aix.ppc</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.motif.aix.ppc64</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.swt.motif.hpux.ia64_32</artifactId>
+                               </exclusion>
+
+                               <!-- Equinox Launcher -->
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.win32.win32.x86_64</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.carbon.macosx</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.cocoa.macosx.x86_64</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.gtk.solaris.sparc</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.gtk.solaris.x86</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.motif.solaris.sparc</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.gtk.linux.s390</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.gtk.linux.s390x</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.gtk.linux.ppc64</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.motif.linux.x86</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.gtk.linux.ppc</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.photon.qnx.x86</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.motif.aix.ppc</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.motif.aix.ppc64</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.equinox.launcher.motif.hpux.ia64_32</artifactId>
+                               </exclusion>
+
+                               <!-- Eclipse UI -->
+                               <exclusion>
+                                       <groupId>org.argeo.tp.rcp.platform</groupId>
+                                       <artifactId>org.eclipse.ui.carbon</artifactId>
+                               </exclusion>
+                       </exclusions>
+               </dependency>
+
+               <!-- Security -->
+               <dependency>
+                       <groupId>org.argeo.tp.rcp</groupId>
+                       <artifactId>org.eclipse.equinox.security</artifactId>
+               </dependency>
+       </dependencies>
+       <dependencyManagement>
+               <dependencies>
+                       <dependency>
+                               <groupId>org.argeo.tp.rcp</groupId>
+                               <artifactId>binaries</artifactId>
+                               <version>${version.argeo-distribution}</version>
+                               <type>pom</type>
+                               <scope>import</scope>
+                       </dependency>
+                       <dependency>
+                               <groupId>org.argeo.tp.rcp.platform</groupId>
+                               <artifactId>binaries</artifactId>
+                               <version>${version.argeo-distribution}</version>
+                               <type>pom</type>
+                               <scope>import</scope>
+                       </dependency>
+               </dependencies>
+       </dependencyManagement>
+       <profiles>
+               <profile>
+                       <id>check-osgi</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.argeo.maven.plugins</groupId>
+                                               <artifactId>maven-argeo-osgi-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>check-osgi</id>
+                                                               <phase>test</phase>
+                                                               <goals>
+                                                                       <goal>equinox</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <onlyCheck>true</onlyCheck>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+               </profile>
+       </profiles>
+</project>
\ No newline at end of file
diff --git a/trunk/base/dep/pom.xml b/trunk/base/dep/pom.xml
new file mode 100644 (file)
index 0000000..12f28eb
--- /dev/null
@@ -0,0 +1,58 @@
+<?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">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <artifactId>base</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <groupId>org.argeo.commons.base</groupId>
+       <artifactId>dep</artifactId>
+       <name>Commons Base Dependencies</name>
+       <packaging>pom</packaging>
+       <modules>
+               <module>org.argeo.dep.log4j</module>
+               <module>org.argeo.dep.jackrabbit</module>
+               <module>org.argeo.eclipse.dep.rap</module>
+               <module>org.argeo.eclipse.dep.rcp</module>
+       </modules>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                       </plugin>
+               </plugins>
+               <pluginManagement>
+                       <plugins>
+                               <plugin>
+                                       <groupId>org.argeo.maven.plugins</groupId>
+                                       <artifactId>maven-argeo-osgi-plugin</artifactId>
+                                       <executions>
+                                               <execution>
+                                                       <id>generate-descriptors</id>
+                                                       <goals>
+                                                               <goal>descriptors</goal>
+                                                       </goals>
+                                                       <phase>generate-resources</phase>
+                                               </execution>
+                                       </executions>
+                               </plugin>
+                       </plugins>
+               </pluginManagement>
+       </build>
+       <profiles>
+               <profile>
+                       <id>check-osgi</id>
+                       <dependencies>
+                               <dependency>
+                                       <groupId>org.argeo.commons.base</groupId>
+                                       <artifactId>org.argeo.osgi.boot</artifactId>
+                                       <version>2.1.11</version>
+                                       <scope>test</scope>
+                               </dependency>
+                       </dependencies>
+               </profile>
+       </profiles>
+</project>
\ No newline at end of file
diff --git a/trunk/base/dist/osgi-boot/pom.xml b/trunk/base/dist/osgi-boot/pom.xml
new file mode 100644 (file)
index 0000000..9c49929
--- /dev/null
@@ -0,0 +1,188 @@
+<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.base</groupId>
+               <version>2.1.11</version>
+               <artifactId>dist</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>osgi-boot</artifactId>
+       <packaging>pom</packaging>
+       <name>Commons OSGi Boot Distribution</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-dependency-plugin</artifactId>
+                               <executions>
+                                       <execution>
+                                               <id>copy-dependencies</id>
+                                               <phase>package</phase>
+                                               <goals>
+                                                       <goal>copy-dependencies</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <includeTypes>jar</includeTypes>
+                                                       <stripVersion>true</stripVersion>
+                                                       <outputDirectory>${project.build.directory}/lib</outputDirectory>
+                                               </configuration>
+                                       </execution>
+                               </executions>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.osgi.boot</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+       </dependencies>
+       <profiles>
+               <profile>
+                       <id>rpmbuild</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.codehaus.mojo</groupId>
+                                               <artifactId>rpm-maven-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>rpm-osgi-boot</id>
+                                                               <phase>package</phase>
+                                                               <goals>
+                                                                       <goal>rpm</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <name>osgi-boot</name>
+                                                                       <mappings>
+                                                                               <mapping>
+                                                                                       <directory>/etc/osgiboot</directory>
+                                                                                       <username>root</username>
+                                                                                       <groupname>root</groupname>
+                                                                                       <filemode>644</filemode>
+                                                                                       <configuration>noreplace</configuration>
+                                                                                       <directoryIncluded>false</directoryIncluded>
+                                                                                       <sources>
+                                                                                               <source>
+                                                                                                       <location>src/main/rpm/etc/osgiboot</location>
+                                                                                                       <includes>
+                                                                                                               <include>*-settings.sh</include>
+                                                                                                       </includes>
+                                                                                               </source>
+                                                                                       </sources>
+                                                                               </mapping>
+                                                                               <mapping>
+                                                                                       <directory>/etc/osgiboot</directory>
+                                                                                       <username>root</username>
+                                                                                       <groupname>root</groupname>
+                                                                                       <filemode>644</filemode>
+                                                                                       <directoryIncluded>false</directoryIncluded>
+                                                                                       <sources>
+                                                                                               <source>
+                                                                                                       <location>src/main/rpm/etc/osgiboot</location>
+                                                                                                       <includes>
+                                                                                                               <include>*-functions.sh</include>
+                                                                                                       </includes>
+                                                                                               </source>
+                                                                                       </sources>
+                                                                               </mapping>
+                                                                               <mapping>
+                                                                                       <directory>/usr/sbin</directory>
+                                                                                       <username>root</username>
+                                                                                       <groupname>root</groupname>
+                                                                                       <filemode>755</filemode>
+                                                                                       <sources>
+                                                                                               <source>
+                                                                                                       <location>src/main/rpm/usr/sbin</location>
+                                                                                               </source>
+                                                                                       </sources>
+                                                                               </mapping>
+                                                                               <mapping>
+                                                                                       <directory>/usr/share/osgi/boot</directory>
+                                                                                       <username>root</username>
+                                                                                       <groupname>root</groupname>
+                                                                                       <filemode>644</filemode>
+                                                                                       <directoryIncluded>false</directoryIncluded>
+                                                                                       <sources>
+                                                                                               <source>
+                                                                                                       <location>${project.build.directory}/lib</location>
+                                                                                                       <includes>
+                                                                                                               <include>org.argeo.osgi.boot.jar</include>
+                                                                                                       </includes>
+                                                                                               </source>
+                                                                                       </sources>
+                                                                               </mapping>
+                                                                       </mappings>
+                                                                       <requires>
+                                                                               <require>osgi-boot-equinox</require>
+                                                                       </requires>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                                       <plugin>
+                                               <artifactId>maven-antrun-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <phase>install</phase>
+                                                               <goals>
+                                                                       <goal>run</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <target>
+                                                                               <copy todir="${rpm.stagingRepository}" verbose="true">
+                                                                                       <fileset dir="${project.build.directory}/rpm" includes="*/RPMS/**/*.rpm" />
+                                                                                       <flattenmapper />
+                                                                               </copy>
+                                                                       </target>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+               </profile>
+               <profile>
+                       <id>rpmbuild-tp</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.codehaus.mojo</groupId>
+                                               <artifactId>rpm-maven-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>rpm-osgi-boot-equinox</id>
+                                                               <phase>package</phase>
+                                                               <goals>
+                                                                       <goal>rpm</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <name>osgi-boot-equinox</name>
+                                                                       <projversion>${version.argeo-distribution}</projversion>
+                                                                       <mappings>
+                                                                               <mapping>
+                                                                                       <directory>/usr/share/osgi/boot</directory>
+                                                                                       <username>root</username>
+                                                                                       <groupname>root</groupname>
+                                                                                       <filemode>644</filemode>
+                                                                                       <directoryIncluded>false</directoryIncluded>
+                                                                                       <sources>
+                                                                                               <source>
+                                                                                                       <location>${project.build.directory}/lib</location>
+                                                                                                       <includes>
+                                                                                                               <include>org.eclipse.osgi.jar</include>
+                                                                                                       </includes>
+                                                                                               </source>
+                                                                                       </sources>
+                                                                               </mapping>
+                                                                       </mappings>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+               </profile>
+       </profiles>
+</project>
diff --git a/trunk/base/dist/osgi-boot/src/main/rpm/etc/osgiboot/osgi-service-init-functions.sh b/trunk/base/dist/osgi-boot/src/main/rpm/etc/osgiboot/osgi-service-init-functions.sh
new file mode 100644 (file)
index 0000000..35bf970
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+RETVAL=0
+
+osgi_service_start() {
+       APP=$1
+       # create log an run directories writable by the application user
+       USER=$APP
+       GROUP=$APP
+       RUN_DIR=/var/run/$APP
+       LOG_DIR=/var/log/$APP
+       if [ ! -d $LOG_DIR ];then
+               mkdir -m 0750 $LOG_DIR
+               chown -R $USER.$GROUP $LOG_DIR
+       fi
+       if [ ! -d $RUN_DIR ];then
+               mkdir -m 0750 $RUN_DIR
+               chown -R $USER.$GROUP $RUN_DIR
+       fi
+       
+       # call Argeo Commons OSGi utilities as the application user
+       daemon --user $USER /usr/sbin/osgi-service $APP start
+       
+       RETVAL=$?
+       #action $"Start $APP" /bin/true
+       if [ $RETVAL -eq 0 ];then
+               PID=`cat $RUN_DIR/$APP.pid`
+               action $"Started $APP with pid $PID" /bin/true
+       else
+               action $"Could not start $APP" /bin/false
+       fi
+       return $RETVAL
+}
+
+osgi_service_stop() {
+       APP=$1
+       USER=$APP
+       # call Argeo Commons OSGi utilities as the application user
+       runuser -s /bin/bash $USER -c "/usr/sbin/osgi-service $APP stop"
+       RETVAL=$?
+       if [ $RETVAL -eq 0 ];then
+               action $"Stopped $APP" /bin/true
+       else
+               action $"Could not stop $APP" /bin/false
+       fi
+       return $RETVAL
+}
+
+osgi_service_status() {
+       APP=$1
+       USER=$APP
+       # call Argeo Commons OSGi utilities as the application user
+       runuser -s /bin/bash $USER -c "/usr/sbin/osgi-service $APP status"
+       RETVAL=$?
+       return $RETVAL
+}
diff --git a/trunk/base/dist/osgi-boot/src/main/rpm/etc/osgiboot/osgi-service-settings.sh b/trunk/base/dist/osgi-boot/src/main/rpm/etc/osgiboot/osgi-service-settings.sh
new file mode 100644 (file)
index 0000000..f5504f8
--- /dev/null
@@ -0,0 +1 @@
+#JAVA_OPTS=-Xmx256m
\ No newline at end of file
diff --git a/trunk/base/dist/osgi-boot/src/main/rpm/usr/sbin/osgi-service b/trunk/base/dist/osgi-boot/src/main/rpm/usr/sbin/osgi-service
new file mode 100644 (file)
index 0000000..5c37f9c
--- /dev/null
@@ -0,0 +1,153 @@
+#!/bin/sh
+
+JVM=java
+. /etc/osgiboot/osgi-service-settings.sh
+
+APP=$1
+
+CONF_DIR=/etc/$APP
+if [ -f $CONF_DIR/settings.sh ];then
+       . $CONF_DIR/settings.sh
+fi
+
+LIB_DIR=/usr/share/$APP/lib
+
+# read/write
+EXEC_DIR=/var/lib/$APP
+DATA_DIR=$EXEC_DIR/data
+CONF_RW=$EXEC_DIR/conf
+
+LOG_DIR=/var/log/$APP
+LOG_FILE=$LOG_DIR/$APP.log
+
+RUN_DIR=/var/run/$APP
+PID_FILE=$RUN_DIR/$APP.pid
+SHUTDOWN_FILE=$RUN_DIR/$APP.shutdown
+
+OSGI_INSTALL_AREA=/usr/share/osgi/boot
+OSGI_FRAMEWORK=$OSGI_INSTALL_AREA/org.eclipse.osgi.jar
+
+RETVAL=0
+
+start() {
+       if [ -f $PID_FILE ];then
+               PID=`cat $PID_FILE`
+               kill -0 $PID &> /dev/null
+               PID_EXISTS=$?
+               if [ $PID_EXISTS -eq 0 ]; then
+                       echo $APP already running with pid $PID
+                       RETVAL=1
+                       return $RETVAL
+               else
+                       echo Old $APP process with pid $PID is dead, removing $PID_FILE
+                       rm -f $PID_FILE
+               fi
+       fi
+
+       cp --preserve $CONF_DIR/config.ini $CONF_RW/config.ini
+       touch $SHUTDOWN_FILE
+       cd $EXEC_DIR
+       $JVM \
+               -Dargeo.osgi.shutdownFile="$SHUTDOWN_FILE" \
+               -Dlog4j.configuration="file:$CONF_DIR/log4j.properties" \
+               $JAVA_OPTS -jar $OSGI_FRAMEWORK \
+               -clean \
+               -configuration "$CONF_RW" \
+               -data "$DATA_DIR" \
+               >> $LOG_FILE 2>&1 &
+       # (above) stderr redirected to stdout, then stdout to log file
+       # see http://tldp.org/LDP/abs/html/io-redirection.html
+       PID=$!
+       echo $PID > $PID_FILE
+       #echo Started $APP with pid $PID
+       return $RETVAL
+}
+
+stop() {
+       if [ -f $PID_FILE ];then
+               PID=`cat $PID_FILE`
+               kill -0 $PID &> /dev/null
+               PID_EXISTS=$?
+               if [ $PID_EXISTS -ne 0 ]; then
+                       echo Dead $APP process with pid $PID, removing $PID_FILE
+                       rm -f $PID_FILE
+                       RETVAL=1
+                       return $RETVAL
+               fi
+       else
+               echo $APP is not running
+               RETVAL=1
+               return $RETVAL
+       fi
+       
+       # notifies application by removing the shutdown file
+       rm -f $SHUTDOWN_FILE
+       
+       # wait 5 min for application to shutdown, then kill it
+       TIMEOUT=$((5*60))
+       BEGIN=$(date +%s)
+       while kill -0 $PID &> /dev/null
+       do
+               sleep 1
+               NOW=$(date +%s)
+               DURATION=$(($NOW-$BEGIN))
+               if [ $DURATION -gt $TIMEOUT ]; then
+                       kill -9 $PID
+                       echo Forcibly killed $APP with pid $PID
+                       RETVAL=1
+               fi
+       done
+       
+       # remove pid file
+       rm -f $PID_FILE
+       return $RETVAL
+
+# timeout is only available in EL6
+#      timeout 5m sh << EOF
+#while kill -0 $PID &> /dev/null; do sleep 1; done
+#EOF
+#      TIMEOUT_EXIT=$?
+#      if [ $TIMEOUT_EXIT -eq 124 ];then
+#              kill -9 $PID
+#              RETVAL=1
+#              echo Killed $APP with pid $PID
+#      else
+#              echo Stopped $APP with pid $PID
+#      fi
+#      rm -f $PID_FILE
+#      return $RETVAL
+}
+
+status() {
+       if [ -f $PID_FILE ];then
+               PID=`cat $PID_FILE`
+       else
+               echo $APP is not running
+               return $RETVAL
+       fi
+       kill -0 $PID &> /dev/null
+       PID_EXISTS=$?
+       if [ $PID_EXISTS -eq 0 ]; then
+               echo $APP is running with pid $PID ...
+       else
+               echo No $APP process with pid $PID, removing $PID_FILE
+               rm -f $PID_FILE
+       fi
+       return $RETVAL
+}
+
+# main
+case "$2" in
+  start)
+        start
+        ;;
+  stop)
+        stop
+        ;;
+  status)
+       status
+        ;;
+  *)
+        echo $"Usage: $0 {start|stop|status}"
+        exit 1
+esac
\ No newline at end of file
diff --git a/trunk/base/dist/pom.xml b/trunk/base/dist/pom.xml
new file mode 100644 (file)
index 0000000..9ef2211
--- /dev/null
@@ -0,0 +1,17 @@
+<?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">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <artifactId>base</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <groupId>org.argeo.commons.base</groupId>
+       <artifactId>dist</artifactId>
+       <name>Commons Base Distributions</name>
+       <packaging>pom</packaging>
+       <modules>
+               <module>osgi-boot</module>
+       </modules>
+</project>
\ No newline at end of file
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/.classpath b/trunk/base/plugins/org.argeo.osgi.ui.explorer/.classpath
new file mode 100644 (file)
index 0000000..c5931a0
--- /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.6"/>>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/.project b/trunk/base/plugins/org.argeo.osgi.ui.explorer/.project
new file mode 100644 (file)
index 0000000..1a4f4d3
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.osgi.ui.explorer</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/trunk/base/plugins/org.argeo.osgi.ui.explorer/.settings/org.eclipse.jdt.core.prefs b/trunk/base/plugins/org.argeo.osgi.ui.explorer/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..9916a22
--- /dev/null
@@ -0,0 +1,8 @@
+#Sun Sep 04 16:04:00 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/build.properties b/trunk/base/plugins/org.argeo.osgi.ui.explorer/build.properties
new file mode 100644 (file)
index 0000000..4f799f2
--- /dev/null
@@ -0,0 +1,6 @@
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .,\
+               icons/,\
+               plugin.xml
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/active.gif b/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/active.gif
new file mode 100644 (file)
index 0000000..7d24707
Binary files /dev/null and b/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/active.gif differ
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/bundles.gif b/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/bundles.gif
new file mode 100644 (file)
index 0000000..e9a6bd9
Binary files /dev/null and b/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/bundles.gif differ
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/installed.gif b/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/installed.gif
new file mode 100644 (file)
index 0000000..2988716
Binary files /dev/null and b/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/installed.gif differ
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/osgi_explorer.gif b/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/osgi_explorer.gif
new file mode 100644 (file)
index 0000000..e9a6bd9
Binary files /dev/null and b/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/osgi_explorer.gif differ
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/resolved.gif b/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/resolved.gif
new file mode 100644 (file)
index 0000000..f4a1ea1
Binary files /dev/null and b/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/resolved.gif differ
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/starting.gif b/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/starting.gif
new file mode 100644 (file)
index 0000000..563743d
Binary files /dev/null and b/trunk/base/plugins/org.argeo.osgi.ui.explorer/icons/starting.gif differ
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/plugin.xml b/trunk/base/plugins/org.argeo.osgi.ui.explorer/plugin.xml
new file mode 100644 (file)
index 0000000..c0815ca
--- /dev/null
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+   <extension
+         point="org.eclipse.ui.perspectives">
+      <perspective
+            name="Monitoring"
+            class="org.argeo.osgi.ui.explorer.OsgiExplorerPerspective"
+            id="org.argeo.osgi.ui.explorer.perspective"
+            icon="icons/osgi_explorer.gif">
+      </perspective>
+   </extension>
+    <extension point="org.eclipse.ui.perspectiveExtensions"> 
+        <perspectiveExtension 
+            targetID="org.argeo.osgi.ui.explorer.perspective"> 
+            <view id="org.argeo.osgi.ui.explorer.bundlesView" minimized="false"
+             ratio="0.5" relationship="left" relative="org.eclipse.ui.editorss"/> 
+            <view id="org.argeo.osgi.ui.explorer.modulesView" minimized="false"
+             relationship="stack" relative="org.argeo.osgi.ui.explorer.bundlesView"/> 
+        </perspectiveExtension> 
+    </extension> 
+   <extension
+         point="org.eclipse.ui.views">
+      <view
+            class="org.argeo.osgi.ui.explorer.views.ModulesView"
+            icon="icons/bundles.gif"
+            id="org.argeo.osgi.ui.explorer.modulesView"
+            name="Modules">
+      </view>
+      <view
+            class="org.argeo.osgi.ui.explorer.views.BundlesView"
+            icon="icons/bundles.gif"
+            id="org.argeo.osgi.ui.explorer.bundlesView"
+            name="Bundles">
+      </view>
+   </extension>
+  <extension
+           point="org.eclipse.ui.activities">
+        <activityPatternBinding
+              activityId="org.argeo.security.ui.adminActivity"
+              isEqualityPattern="true"
+              pattern="org.argeo.osgi.ui.explorer/org.argeo.osgi.ui.explorer.perspective">
+        </activityPatternBinding>
+     </extension>
+</plugin>
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/pom.xml b/trunk/base/plugins/org.argeo.osgi.ui.explorer/pom.xml
new file mode 100644 (file)
index 0000000..95e3bc3
--- /dev/null
@@ -0,0 +1,56 @@
+<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.base</groupId>
+               <version>2.1.11</version>
+               <artifactId>plugins</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.osgi.ui.explorer</artifactId>
+       <name>Commons OSGi Explorer</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-ActivationPolicy>lazy</Bundle-ActivationPolicy>
+                                               <Bundle-Activator>org.argeo.osgi.ui.explorer.OsgiExplorerPlugin</Bundle-Activator>
+                                               <Require-Bundle>org.eclipse.ui;resolution:=optional,
+                                                       org.eclipse.rap.ui;resolution:=optional,
+                                                       org.eclipse.core.runtime</Require-Bundle>
+                                               <Export-Package>org.argeo.osgi.ui.explorer.*</Export-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.eclipse.osgi</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- RCP only dependency, needed at compile time -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.dep.rcp</artifactId>
+                       <version>2.1.11</version>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui.rcp</artifactId>
+                       <version>2.1.11</version>
+                       <scope>provided</scope>
+               </dependency>
+       </dependencies>
+</project>
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/OsgiExplorerImages.java b/trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/OsgiExplorerImages.java
new file mode 100644 (file)
index 0000000..77bdfe9
--- /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.osgi.ui.explorer;
+
+import org.eclipse.swt.graphics.Image;
+
+/** Shared icons. */
+public class OsgiExplorerImages {
+       public final static Image INSTALLED = OsgiExplorerPlugin
+                       .getImageDescriptor("icons/installed.gif").createImage();
+       public final static Image RESOLVED = OsgiExplorerPlugin.getImageDescriptor(
+                       "icons/resolved.gif").createImage();
+       public final static Image STARTING = OsgiExplorerPlugin.getImageDescriptor(
+                       "icons/starting.gif").createImage();
+       public final static Image ACTIVE = OsgiExplorerPlugin.getImageDescriptor(
+                       "icons/active.gif").createImage();
+}
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/OsgiExplorerPerspective.java b/trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/OsgiExplorerPerspective.java
new file mode 100644 (file)
index 0000000..153abcd
--- /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.osgi.ui.explorer;
+
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+
+/** OSGi explorer perspective (to be enriched declaratively) */
+public class OsgiExplorerPerspective implements IPerspectiveFactory {
+
+       public void createInitialLayout(IPageLayout layout) {
+               layout.setEditorAreaVisible(true);
+               layout.setFixed(false);
+
+       }
+
+}
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/OsgiExplorerPlugin.java b/trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/OsgiExplorerPlugin.java
new file mode 100644 (file)
index 0000000..80146d1
--- /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.osgi.ui.explorer;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class OsgiExplorerPlugin extends AbstractUIPlugin {
+
+       // The plug-in ID
+       public static final String PLUGIN_ID = "org.argeo.osgi.ui.explorer"; //$NON-NLS-1$
+
+       // The shared instance
+       private static OsgiExplorerPlugin plugin;
+
+       /**
+        * The constructor
+        */
+       public OsgiExplorerPlugin() {
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see
+        * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext
+        * )
+        */
+       public void start(BundleContext context) throws Exception {
+               super.start(context);
+               plugin = this;
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see
+        * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext
+        * )
+        */
+       public void stop(BundleContext context) throws Exception {
+               plugin = null;
+               super.stop(context);
+       }
+
+       /**
+        * Returns the shared instance
+        * 
+        * @return the shared instance
+        */
+       public static OsgiExplorerPlugin getDefault() {
+               return plugin;
+       }
+
+       public static ImageDescriptor getImageDescriptor(String path) {
+               return imageDescriptorFromPlugin(PLUGIN_ID, path);
+       }
+
+}
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/views/BundlesView.java b/trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/views/BundlesView.java
new file mode 100644 (file)
index 0000000..98d74a6
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * 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.osgi.ui.explorer.views;
+
+import java.util.Comparator;
+
+import org.argeo.eclipse.ui.ColumnViewerComparator;
+import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils;
+import org.argeo.osgi.ui.explorer.OsgiExplorerImages;
+import org.argeo.osgi.ui.explorer.OsgiExplorerPlugin;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.part.ViewPart;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+/**
+ * Overview of the bundles as a table. Equivalent to Equinox 'ss' console
+ * command.
+ */
+public class BundlesView extends ViewPart {
+       private TableViewer viewer;
+
+       @Override
+       public void createPartControl(Composite parent) {
+               viewer = new TableViewer(parent);
+               viewer.setContentProvider(new BundleContentProvider());
+               viewer.getTable().setHeaderVisible(true);
+
+               EclipseUiSpecificUtils.enableToolTipSupport(viewer);
+
+               // ID
+               TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
+               column.getColumn().setWidth(30);
+               column.getColumn().setText("ID");
+               column.getColumn().setAlignment(SWT.RIGHT);
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       public String getText(Object element) {
+                               return Long.toString(((Bundle) element).getBundleId());
+                       }
+               });
+               new ColumnViewerComparator<Bundle>(column, new Comparator<Bundle>() {
+                       public int compare(Bundle o1, Bundle o2) {
+                               return (int) (o1.getBundleId() - o2.getBundleId());
+                       }
+               });
+
+               // State
+               column = new TableViewerColumn(viewer, SWT.NONE);
+               column.getColumn().setWidth(18);
+               column.getColumn().setText("State");
+               column.setLabelProvider(new StateLabelProvider());
+               new ColumnViewerComparator<Bundle>(column, new Comparator<Bundle>() {
+                       public int compare(Bundle o1, Bundle o2) {
+                               return o1.getState() - o2.getState();
+                       }
+               });
+
+               // Symbolic name
+               column = new TableViewerColumn(viewer, SWT.NONE);
+               column.getColumn().setWidth(300);
+               column.getColumn().setText("Symbolic Name");
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       public String getText(Object element) {
+                               return ((Bundle) element).getSymbolicName();
+                       }
+               });
+               new ColumnViewerComparator<Bundle>(column, new Comparator<Bundle>() {
+                       public int compare(Bundle o1, Bundle o2) {
+                               return o1.getSymbolicName().compareTo(o2.getSymbolicName());
+                       }
+               });
+
+               // Version
+               column = new TableViewerColumn(viewer, SWT.NONE);
+               column.getColumn().setWidth(150);
+               column.getColumn().setText("Version");
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       public String getText(Object element) {
+                               return ((Bundle) element).getVersion().toString();
+                       }
+               });
+               new ColumnViewerComparator<Bundle>(column, new Comparator<Bundle>() {
+                       public int compare(Bundle o1, Bundle o2) {
+                               return o1.getVersion().compareTo(o2.getVersion());
+                       }
+               });
+
+               viewer.setInput(OsgiExplorerPlugin.getDefault().getBundle()
+                               .getBundleContext());
+
+       }
+
+       @Override
+       public void setFocus() {
+               if (viewer != null)
+                       viewer.getControl().setFocus();
+       }
+
+       /** Content provider managing the array of bundles */
+       private static class BundleContentProvider implements
+                       IStructuredContentProvider {
+               public Object[] getElements(Object inputElement) {
+                       if (inputElement instanceof BundleContext) {
+                               BundleContext bc = (BundleContext) inputElement;
+                               return bc.getBundles();
+                       }
+                       return null;
+               }
+
+               public void dispose() {
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+
+       }
+
+       /** Label provider for the state column */
+       private static class StateLabelProvider extends ColumnLabelProvider {
+               @Override
+               public Image getImage(Object element) {
+                       Integer state = ((Bundle) element).getState();
+                       switch (state) {
+                       case Bundle.UNINSTALLED:
+                               return OsgiExplorerImages.INSTALLED;
+                       case Bundle.INSTALLED:
+                               return OsgiExplorerImages.INSTALLED;
+                       case Bundle.RESOLVED:
+                               return OsgiExplorerImages.RESOLVED;
+                       case Bundle.STARTING:
+                               return OsgiExplorerImages.STARTING;
+                       case Bundle.STOPPING:
+                               return OsgiExplorerImages.STARTING;
+                       case Bundle.ACTIVE:
+                               return OsgiExplorerImages.ACTIVE;
+                       default:
+                               return null;
+                       }
+               }
+
+               @Override
+               public String getText(Object element) {
+                       return null;
+               }
+
+               @Override
+               public String getToolTipText(Object element) {
+                       Bundle bundle = (Bundle) element;
+                       Integer state = bundle.getState();
+                       switch (state) {
+                       case Bundle.UNINSTALLED:
+                               return "UNINSTALLED";
+                       case Bundle.INSTALLED:
+                               return "INSTALLED";
+                       case Bundle.RESOLVED:
+                               return "RESOLVED";
+                       case Bundle.STARTING:
+                               String activationPolicy = bundle.getHeaders()
+                                               .get(Constants.BUNDLE_ACTIVATIONPOLICY).toString();
+                               if (activationPolicy != null
+                                               && activationPolicy.equals(Constants.ACTIVATION_LAZY))
+                                       return "<<LAZY>>";
+                               return "STARTING";
+                       case Bundle.STOPPING:
+                               return "STOPPING";
+                       case Bundle.ACTIVE:
+                               return "ACTIVE";
+                       default:
+                               return null;
+                       }
+               }
+
+       }
+}
diff --git a/trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/views/ModulesView.java b/trunk/base/plugins/org.argeo.osgi.ui.explorer/src/main/java/org/argeo/osgi/ui/explorer/views/ModulesView.java
new file mode 100644 (file)
index 0000000..5d67bd4
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * 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.osgi.ui.explorer.views;
+
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.eclipse.ui.TreeParent;
+import org.argeo.osgi.ui.explorer.OsgiExplorerPlugin;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.part.ViewPart;
+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;
+
+/** <b>Experimental</b> The OSGi runtime from a module perspective. */
+public class ModulesView extends ViewPart {
+       private final static Log log = LogFactory.getLog(ModulesView.class);
+
+       private TreeViewer viewer;
+
+       private PackageAdmin packageAdmin;
+
+       private Comparator<ExportedPackage> exportedPackageComparator = new Comparator<ExportedPackage>() {
+
+               public int compare(ExportedPackage o1, ExportedPackage o2) {
+                       if (!o1.getName().equals(o2.getName()))
+                               return o1.getName().compareTo(o2.getName());
+                       else
+                               return o1.getVersion().compareTo(o2.getVersion());
+               }
+       };
+
+       @Override
+       public void createPartControl(Composite parent) {
+               viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+               viewer.setContentProvider(new ModulesContentProvider());
+               viewer.setLabelProvider(new ModulesLabelProvider());
+               viewer.setInput(OsgiExplorerPlugin.getDefault().getBundle()
+                               .getBundleContext());
+       }
+
+       @Override
+       public void setFocus() {
+               viewer.getTree().setFocus();
+       }
+
+       private class ModulesContentProvider implements ITreeContentProvider {
+
+               public Object[] getElements(Object inputElement) {
+                       return getChildren(inputElement);
+               }
+
+               public Object[] getChildren(Object parentElement) {
+                       if (parentElement instanceof BundleContext) {
+                               BundleContext bundleContext = (BundleContext) parentElement;
+                               Bundle[] bundles = bundleContext.getBundles();
+
+                               TreeParent bundlesNode = new TreeParent("Bundles");
+                               for (Bundle bundle : bundles) {
+                                       if (bundle.getState() == Bundle.ACTIVE)
+                                               bundlesNode.addChild(new BundleNode(bundle));
+                               }
+
+                               // scan packages
+                               ServiceReference paSr = bundleContext
+                                               .getServiceReference(PackageAdmin.class.getName());
+                               // TODO: make a cleaner referencing
+                               packageAdmin = (PackageAdmin) bundleContext.getService(paSr);
+
+                               Bundle bundle1 = null;
+                               Bundle bundle2 = null;
+
+                               Map<Bundle, Set<ExportedPackage>> importedPackages = new HashMap<Bundle, Set<ExportedPackage>>();
+                               Map<String, Set<ExportedPackage>> packages = new TreeMap<String, Set<ExportedPackage>>();
+                               for (Bundle bundle : bundles) {
+                                       if (bundle.getSymbolicName()
+                                                       .equals("org.argeo.security.ui"))
+                                               bundle1 = bundle;
+                                       if (bundle.getSymbolicName().equals(
+                                                       "org.argeo.security.equinox"))
+                                               bundle2 = bundle;
+
+                                       ExportedPackage[] pkgs = packageAdmin
+                                                       .getExportedPackages(bundle);
+                                       if (pkgs != null)
+                                               for (ExportedPackage pkg : pkgs) {
+                                                       if (!packages.containsKey(pkg.getName()))
+                                                               packages.put(pkg.getName(),
+                                                                               new TreeSet<ExportedPackage>(
+                                                                                               exportedPackageComparator));
+                                                       Set<ExportedPackage> expPackages = (Set<ExportedPackage>) packages
+                                                                       .get(pkg.getName());
+                                                       expPackages.add(pkg);
+
+                                                       // imported
+                                                       for (Bundle b : pkg.getImportingBundles()) {
+                                                               if (bundle.getBundleId() != b.getBundleId()) {
+                                                                       if (!importedPackages.containsKey(b))
+                                                                               importedPackages
+                                                                                               .put(b,
+                                                                                                               new TreeSet<ExportedPackage>(
+                                                                                                                               exportedPackageComparator));
+                                                                       Set<ExportedPackage> impPackages = (Set<ExportedPackage>) importedPackages
+                                                                                       .get(b);
+                                                                       impPackages.add(pkg);
+                                                               }
+                                                       }
+                                               }
+                               }
+
+                               TreeParent mPackageNode = new TreeParent("Multiple Packages");
+                               // TreeParent aPackageNode = new TreeParent("All Packages");
+                               for (String packageName : packages.keySet()) {
+                                       Set<ExportedPackage> pkgs = packages.get(packageName);
+                                       if (pkgs.size() > 1) {
+                                               MultiplePackagesNode mpn = new MultiplePackagesNode(
+                                                               packageName, pkgs);
+                                               mPackageNode.addChild(mpn);
+                                               // aPackageNode.addChild(mpn);
+                                       } else {
+                                               // aPackageNode.addChild(new ExportedPackageNode(pkgs
+                                               // .iterator().next()));
+                                       }
+                               }
+
+                               return new Object[] { bundlesNode, mPackageNode };// ,
+                                                                                                                                       // aPackageNode
+                                                                                                                                       // };
+                       } else if (parentElement instanceof TreeParent) {
+                               return ((TreeParent) parentElement).getChildren();
+                       } else {
+                               return null;
+                       }
+               }
+
+               public Object getParent(Object element) {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public boolean hasChildren(Object element) {
+                       if (element instanceof TreeParent) {
+                               return ((TreeParent) element).hasChildren();
+                       }
+                       return false;
+               }
+
+               public void dispose() {
+                       // TODO Auto-generated method stub
+
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+                       // TODO Auto-generated method stub
+
+               }
+
+       }
+
+       protected Map<String, ExportedPackage> dependencySpace(Bundle bundle,
+                       Map<Bundle, Set<ExportedPackage>> importedPackages,
+                       Map<String, Set<String>> traces) {
+               log.debug("Dependency space for " + bundle.getSymbolicName());
+               Map<String, ExportedPackage> space = new TreeMap<String, ExportedPackage>();
+               fillDependencySpace(space, bundle, importedPackages,
+                               bundle.getSymbolicName(), traces);
+               return space;
+       }
+
+       /** Recursive */
+       protected void fillDependencySpace(Map<String, ExportedPackage> space,
+                       Bundle bundle, Map<Bundle, Set<ExportedPackage>> importedPackages,
+                       String currTrace, Map<String, Set<String>> traces) {
+               if (importedPackages.containsKey(bundle)) {
+                       Set<ExportedPackage> imports = importedPackages.get(bundle);
+                       // log.debug("## Fill dependency space for " + bundle + " : ");
+                       for (ExportedPackage pkg : imports) {
+                               if (!traces.containsKey(pkg.getName()))
+                                       traces.put(pkg.getName(), new TreeSet<String>());
+                               traces.get(pkg.getName()).add(currTrace);
+                               if (!space.containsKey(pkg.getName())) {
+                                       space.put(pkg.getName(), pkg);
+                                       Bundle exportingBundle = pkg.getExportingBundle();
+                                       // if (bundle.getBundleId() !=
+                                       // exportingBundle.getBundleId())
+                                       fillDependencySpace(space, exportingBundle,
+                                                       importedPackages, currTrace + " > "
+                                                                       + exportingBundle.getSymbolicName(), traces);
+                               }
+                       }
+               }
+       }
+
+       private class ModulesLabelProvider extends LabelProvider implements
+                       ITableLabelProvider {
+
+               public Image getColumnImage(Object element, int columnIndex) {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getColumnText(Object element, int columnIndex) {
+                       return getText(element);
+               }
+
+       }
+
+       class BundleNode extends TreeParent {
+               private final Bundle bundle;
+
+               public BundleNode(Bundle bundle) {
+                       super(bundle.getSymbolicName());
+                       this.bundle = bundle;
+
+                       // Registered services
+                       ServiceReference[] registeredServices = bundle
+                                       .getRegisteredServices();
+                       if (registeredServices != null) {
+                               TreeParent registeredServicesNode = new TreeParent(
+                                               "Registered Services");
+                               addChild(registeredServicesNode);
+                               for (ServiceReference sr : registeredServices) {
+                                       if (sr != null)
+                                               registeredServicesNode
+                                                               .addChild(new ServiceReferenceNode(sr));
+                               }
+                       }
+
+                       // Used services
+                       ServiceReference[] usedServices = bundle.getRegisteredServices();
+                       if (usedServices != null) {
+                               TreeParent usedServicesNode = new TreeParent("Used Services");
+                               addChild(usedServicesNode);
+                               for (ServiceReference sr : usedServices) {
+                                       if (sr != null)
+                                               usedServicesNode.addChild(new ServiceReferenceNode(sr));
+                               }
+                       }
+               }
+
+               public Bundle getBundle() {
+                       return bundle;
+               }
+
+       }
+
+       class ServiceReferenceNode extends TreeParent {
+               private final ServiceReference serviceReference;
+
+               public ServiceReferenceNode(ServiceReference serviceReference) {
+                       super(serviceReference.toString());
+                       this.serviceReference = serviceReference;
+
+                       Bundle[] usedBundles = serviceReference.getUsingBundles();
+                       if (usedBundles != null) {
+                               TreeParent usingBundles = new TreeParent("Using Bundles");
+                               addChild(usingBundles);
+                               for (Bundle b : usedBundles) {
+                                       if (b != null)
+                                               usingBundles.addChild(new TreeParent(b
+                                                               .getSymbolicName()));
+                               }
+                       }
+
+                       TreeParent properties = new TreeParent("Properties");
+                       addChild(properties);
+                       for (String key : serviceReference.getPropertyKeys()) {
+                               properties.addChild(new TreeParent(key + "="
+                                               + serviceReference.getProperty(key)));
+                       }
+
+               }
+
+               public ServiceReference getServiceReference() {
+                       return serviceReference;
+               }
+
+       }
+
+       class MultiplePackagesNode extends TreeParent {
+               private String packageName;
+               private Set<ExportedPackage> exportedPackages;
+
+               public MultiplePackagesNode(String packageName,
+                               Set<ExportedPackage> exportedPackages) {
+                       super(packageName);
+                       this.packageName = packageName;
+                       this.exportedPackages = exportedPackages;
+                       for (ExportedPackage pkg : exportedPackages) {
+                               addChild(new ExportedPackageNode(pkg));
+                       }
+               }
+
+       }
+
+       class ConflictingPackageNode extends TreeParent {
+               private ExportedPackage exportedPackage;
+
+               public ConflictingPackageNode(ExportedPackage exportedPackage) {
+                       super(exportedPackage.getName() + " - "
+                                       + exportedPackage.getVersion() + " ("
+                                       + exportedPackage.getExportingBundle() + ")");
+                       this.exportedPackage = exportedPackage;
+
+                       TreeParent bundlesNode = new TreeParent("Dependent Bundles");
+                       this.addChild(bundlesNode);
+                       Map<String, Bundle> bundles = new TreeMap<String, Bundle>();
+                       for (Bundle b : exportedPackage.getImportingBundles()) {
+                               bundles.put(b.getSymbolicName(), b);
+                       }
+                       for (String key : bundles.keySet()) {
+                               addDependentBundles(bundlesNode, bundles.get(key));
+                       }
+               }
+       }
+
+       protected void addDependentBundles(TreeParent parent, Bundle bundle) {
+               TreeParent bundleNode = new TreeParent(bundle.toString());
+               parent.addChild(bundleNode);
+               Map<String, Bundle> bundles = new TreeMap<String, Bundle>();
+               ExportedPackage[] pkgs = packageAdmin.getExportedPackages(bundle);
+               if (pkgs != null)
+                       for (ExportedPackage pkg : pkgs) {
+                               for (Bundle b : pkg.getImportingBundles()) {
+                                       if (!bundles.containsKey(b.getSymbolicName())
+                                                       && b.getBundleId() != bundle.getBundleId()) {
+                                               bundles.put(b.getSymbolicName(), b);
+                                       }
+                               }
+                       }
+
+               for (String key : bundles.keySet()) {
+                       addDependentBundles(bundleNode, bundles.get(key));
+               }
+       }
+
+       class ExportedPackageNode extends TreeParent {
+               private ExportedPackage exportedPackage;
+
+               public ExportedPackageNode(ExportedPackage exportedPackage) {
+                       super(exportedPackage.getName() + " - "
+                                       + exportedPackage.getVersion() + " ("
+                                       + exportedPackage.getExportingBundle() + ")");
+                       this.exportedPackage = exportedPackage;
+                       for (Bundle bundle : exportedPackage.getImportingBundles()) {
+                               addChild(new BundleNode(bundle));
+                       }
+               }
+       }
+}
diff --git a/trunk/base/plugins/pom.xml b/trunk/base/plugins/pom.xml
new file mode 100644 (file)
index 0000000..2b00b58
--- /dev/null
@@ -0,0 +1,40 @@
+<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>base</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <groupId>org.argeo.commons.base</groupId>
+       <artifactId>plugins</artifactId>
+       <name>Commons OSGi Eclipse Plugins</name>
+       <packaging>pom</packaging>
+       <modules>
+               <module>org.argeo.osgi.ui.explorer</module>
+       </modules>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-SymbolicName>${project.artifactId};singleton:=true</Bundle-SymbolicName>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                               <configuration>
+                                       <!-- Prevents source jars to contain misleading data -->
+                                       <excludes>
+                                               <exclude>plugin.xml</exclude>
+                                               <exclude>META-INF/MANIFEST.MF</exclude>
+                                       </excludes>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
diff --git a/trunk/base/pom.xml b/trunk/base/pom.xml
new file mode 100644 (file)
index 0000000..36c632a
--- /dev/null
@@ -0,0 +1,20 @@
+<?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">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <version>2.1.11</version>
+               <artifactId>argeo-commons</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>base</artifactId>
+       <name>Commons Base</name>
+       <description>Standalone building blocks, extending Java or some libraries</description>
+       <packaging>pom</packaging>
+       <modules>
+               <module>dep</module>
+               <module>runtime</module>
+               <module>plugins</module>
+               <module>dist</module>
+       </modules>
+</project>
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/.classpath b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/.classpath
new file mode 100644 (file)
index 0000000..cf1efe7
--- /dev/null
@@ -0,0 +1,8 @@
+<?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.6"/>>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="src" path="src/main/resources"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/.project b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/.project
new file mode 100644 (file)
index 0000000..d152683
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.eclipse.ui.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/trunk/base/runtime/org.argeo.eclipse.ui.jcr/.settings/org.eclipse.jdt.core.prefs b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..57d3f00
--- /dev/null
@@ -0,0 +1,8 @@
+#Sat Jan 22 14:25:47 CET 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/.settings/org.eclipse.pde.core.prefs b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..81635e9
--- /dev/null
@@ -0,0 +1,4 @@
+#Sat Jan 22 14:25:47 CET 2011
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/build.properties b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/build.properties
new file mode 100644 (file)
index 0000000..0f2736b
--- /dev/null
@@ -0,0 +1,6 @@
+source.. = src/main/java/,\
+                       src/main/resources
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .,\
+               icons/
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/binary.png b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/binary.png
new file mode 100644 (file)
index 0000000..fdf4f82
Binary files /dev/null and b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/binary.png differ
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/file.gif b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/file.gif
new file mode 100644 (file)
index 0000000..ef30288
Binary files /dev/null and b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/file.gif differ
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/folder.gif b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/folder.gif
new file mode 100644 (file)
index 0000000..42e027c
Binary files /dev/null and b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/folder.gif differ
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/home.gif b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/home.gif
new file mode 100644 (file)
index 0000000..fd0c669
Binary files /dev/null and b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/home.gif differ
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/node.gif b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/node.gif
new file mode 100644 (file)
index 0000000..364c0e7
Binary files /dev/null and b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/node.gif differ
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/remote_connected.gif b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/remote_connected.gif
new file mode 100644 (file)
index 0000000..1492b4e
Binary files /dev/null and b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/remote_connected.gif differ
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/remote_disconnected.gif b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/remote_disconnected.gif
new file mode 100644 (file)
index 0000000..6c54da9
Binary files /dev/null and b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/remote_disconnected.gif differ
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/repositories.gif b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/repositories.gif
new file mode 100644 (file)
index 0000000..c13bea1
Binary files /dev/null and b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/repositories.gif differ
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/repository_connected.gif b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/repository_connected.gif
new file mode 100644 (file)
index 0000000..a15fa55
Binary files /dev/null and b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/repository_connected.gif differ
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/repository_disconnected.gif b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/repository_disconnected.gif
new file mode 100644 (file)
index 0000000..4576dc5
Binary files /dev/null and b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/repository_disconnected.gif differ
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/sort.gif b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/sort.gif
new file mode 100644 (file)
index 0000000..23c5d0b
Binary files /dev/null and b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/sort.gif differ
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/workspace_connected.png b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/workspace_connected.png
new file mode 100644 (file)
index 0000000..0430baa
Binary files /dev/null and b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/workspace_connected.png differ
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/workspace_disconnected.png b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/workspace_disconnected.png
new file mode 100644 (file)
index 0000000..fddcb8c
Binary files /dev/null and b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/icons/workspace_disconnected.png differ
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/pom.xml b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/pom.xml
new file mode 100644 (file)
index 0000000..4ff27f4
--- /dev/null
@@ -0,0 +1,89 @@
+<?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.base</groupId>
+               <version>2.1.11</version>
+               <artifactId>runtime</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.eclipse.ui.jcr</artifactId>
+       <name>Commons Eclipse UI JCR</name>
+       <packaging>jar</packaging>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-ActivationPolicy>lazy</Bundle-ActivationPolicy>
+                                               <Require-Bundle>org.eclipse.ui;resolution:=optional,org.eclipse.rap.ui;resolution:=optional,org.eclipse.core.runtime</Require-Bundle>
+                                               <Import-Package>
+                                                       org.eclipse.swt,
+                                                       org.argeo.eclipse.ui.specific,
+                                                       *
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- JCR -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.jcr</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.jcr</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- RCP only dependency, needed at compile time -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.dep.rcp</artifactId>
+                       <version>2.1.11</version>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui.rcp</artifactId>
+                       <version>2.1.11</version>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- Argeo Commons -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java
new file mode 100644 (file)
index 0000000..ef2b8a8
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * 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.eclipse.ui.jcr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.AbstractTreeContentProvider;
+
+/** Canonic implementation of tree content provider manipulating JCR nodes. */
+public abstract class AbstractNodeContentProvider extends
+               AbstractTreeContentProvider {
+       private final static Log log = LogFactory
+                       .getLog(AbstractNodeContentProvider.class);
+
+       private Session session;
+
+       public AbstractNodeContentProvider(Session session) {
+               this.session = session;
+       }
+
+       /**
+        * Whether this path is a base path (and thus has no parent). By default it
+        * returns true if path is '/' (root node)
+        */
+       protected Boolean isBasePath(String path) {
+               // root node
+               return path.equals("/");
+       }
+
+       @Override
+       public Object[] getChildren(Object element) {
+               Object[] children;
+               if (element instanceof Node) {
+                       try {
+                               Node node = (Node) element;
+                               children = getChildren(node);
+                       } catch (RepositoryException e) {
+                               throw new ArgeoException("Cannot get children of " + element, e);
+                       }
+               } else if (element instanceof WrappedNode) {
+                       WrappedNode wrappedNode = (WrappedNode) element;
+                       try {
+                               children = getChildren(wrappedNode.getNode());
+                       } catch (RepositoryException e) {
+                               throw new ArgeoException("Cannot get children of "
+                                               + wrappedNode, e);
+                       }
+               } else if (element instanceof NodesWrapper) {
+                       NodesWrapper node = (NodesWrapper) element;
+                       children = node.getChildren();
+               } else {
+                       children = super.getChildren(element);
+               }
+
+               children = sort(element, children);
+               return children;
+       }
+
+       /** Do not sort by default. To be overidden to provide custom sort. */
+       protected Object[] sort(Object parent, Object[] children) {
+               return children;
+       }
+
+       /**
+        * To be overridden in order to filter out some nodes. Does nothing by
+        * default. The provided list is a temporary one and can thus be modified
+        * directly . (e.g. via an iterator)
+        */
+       protected List<Node> filterChildren(List<Node> children)
+                       throws RepositoryException {
+               return children;
+       }
+
+       protected Object[] getChildren(Node node) throws RepositoryException {
+               List<Node> nodes = new ArrayList<Node>();
+               for (NodeIterator nit = node.getNodes(); nit.hasNext();)
+                       nodes.add(nit.nextNode());
+               nodes = filterChildren(nodes);
+               return nodes.toArray();
+       }
+
+       @Override
+       public Object getParent(Object element) {
+               if (element instanceof Node) {
+                       Node node = (Node) element;
+                       try {
+                               String path = node.getPath();
+                               if (isBasePath(path))
+                                       return null;
+                               else
+                                       return node.getParent();
+                       } catch (RepositoryException e) {
+                               log.warn("Cannot get parent of " + element + ": " + e);
+                               return null;
+                       }
+               } else if (element instanceof WrappedNode) {
+                       WrappedNode wrappedNode = (WrappedNode) element;
+                       return wrappedNode.getParent();
+               } else if (element instanceof NodesWrapper) {
+                       NodesWrapper nodesWrapper = (NodesWrapper) element;
+                       return this.getParent(nodesWrapper.getNode());
+               }
+               return super.getParent(element);
+       }
+
+       @Override
+       public boolean hasChildren(Object element) {
+               try {
+                       if (element instanceof Node) {
+                               Node node = (Node) element;
+                               return node.hasNodes();
+                       } else if (element instanceof WrappedNode) {
+                               WrappedNode wrappedNode = (WrappedNode) element;
+                               return wrappedNode.getNode().hasNodes();
+                       } else if (element instanceof NodesWrapper) {
+                               NodesWrapper nodesWrapper = (NodesWrapper) element;
+                               return nodesWrapper.hasChildren();
+                       }
+
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot check whether " + element
+                                       + " has children", e);
+               }
+               return super.hasChildren(element);
+       }
+
+       public Session getSession() {
+               return session;
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java
new file mode 100644 (file)
index 0000000..100ceb4
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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.eclipse.ui.jcr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.eclipse.swt.widgets.Display;
+
+/** {@link EventListener} which simplifies running actions within the UI thread. */
+public abstract class AsyncUiEventListener implements EventListener {
+//     private final static Log logSuper = LogFactory
+//                     .getLog(AsyncUiEventListener.class);
+       private final Log logThis = LogFactory.getLog(getClass());
+
+       private final Display display;
+
+       public AsyncUiEventListener(Display display) {
+               super();
+               this.display = display;
+       }
+
+       /** Called asynchronously in the UI thread. */
+       protected abstract void onEventInUiThread(List<Event> events)
+                       throws RepositoryException;
+
+       /**
+        * Whether these events should be processed in the UI or skipped with no UI
+        * job created.
+        */
+       protected Boolean willProcessInUiThread(List<Event> events)
+                       throws RepositoryException {
+               return true;
+       }
+
+       protected Log getLog() {
+               return logThis;
+       }
+
+       public final void onEvent(final EventIterator eventIterator) {
+               final List<Event> events = new ArrayList<Event>();
+               while (eventIterator.hasNext())
+                       events.add(eventIterator.nextEvent());
+
+               if (logThis.isTraceEnabled())
+                       logThis.trace("Received " + events.size() + " events");
+
+               try {
+                       if (!willProcessInUiThread(events))
+                               return;
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot test skip events " + events, e);
+               }
+
+//             Job job = new Job("JCR Events") {
+//                     protected IStatus run(IProgressMonitor monitor) {
+//                             if (display.isDisposed()) {
+//                                     logSuper.warn("Display is disposed cannot update UI");
+//                                     return Status.CANCEL_STATUS;
+//                             }
+
+                               display.asyncExec(new Runnable() {
+                                       public void run() {
+                                               try {
+                                                       onEventInUiThread(events);
+                                               } catch (RepositoryException e) {
+                                                       throw new ArgeoException("Cannot process events "
+                                                                       + events, e);
+                                               }
+                                       }
+                               });
+
+//                             return Status.OK_STATUS;
+//                     }
+//             };
+//             job.schedule();
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java
new file mode 100644 (file)
index 0000000..76fac12
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.eclipse.ui.jcr;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoTypes;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.swt.graphics.Image;
+
+/** Provides reasonable overridable defaults for know JCR types. */
+public class DefaultNodeLabelProvider extends ColumnLabelProvider {
+       // Images
+       /**
+        * @deprecated Use {@link JcrImages#NODE} instead
+        */
+       public final static Image NODE = JcrImages.NODE;
+       /**
+        * @deprecated Use {@link JcrImages#FOLDER} instead
+        */
+       public final static Image FOLDER = JcrImages.FOLDER;
+       /**
+        * @deprecated Use {@link JcrImages#FILE} instead
+        */
+       public final static Image FILE = JcrImages.FILE;
+       /**
+        * @deprecated Use {@link JcrImages#BINARY} instead
+        */
+       public final static Image BINARY = JcrImages.BINARY;
+       /**
+        * @deprecated Use {@link JcrImages#HOME} instead
+        */
+       public final static Image HOME = JcrImages.HOME;
+
+       public String getText(Object element) {
+               try {
+                       if (element instanceof Node) {
+                               return getText((Node) element);
+                       } else if (element instanceof WrappedNode) {
+                               return getText(((WrappedNode) element).getNode());
+                       } else if (element instanceof NodesWrapper) {
+                               return getText(((NodesWrapper) element).getNode());
+                       }
+                       return super.getText(element);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot get text for of " + element, e);
+               }
+       }
+
+       protected String getText(Node node) throws RepositoryException {
+               if (node.isNodeType(NodeType.MIX_TITLE)
+                               && node.hasProperty(Property.JCR_TITLE))
+                       return node.getProperty(Property.JCR_TITLE).getString();
+               else
+                       return node.getName();
+       }
+
+       @Override
+       public Image getImage(Object element) {
+               try {
+                       if (element instanceof Node) {
+                               return getImage((Node) element);
+                       } else if (element instanceof WrappedNode) {
+                               return getImage(((WrappedNode) element).getNode());
+                       } else if (element instanceof NodesWrapper) {
+                               return getImage(((NodesWrapper) element).getNode());
+                       }
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot retrieve image for " + element, e);
+               }
+               return super.getImage(element);
+       }
+
+       protected Image getImage(Node node) throws RepositoryException {
+               // optimized order
+               if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FILE))
+                       return JcrImages.FILE;
+               else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER))
+                       return JcrImages.FOLDER;
+               else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_RESOURCE))
+                       return JcrImages.BINARY;
+               else if (node.isNodeType(ArgeoTypes.ARGEO_USER_HOME))
+                       return JcrImages.HOME;
+               else
+                       return JcrImages.NODE;
+       }
+
+       @Override
+       public String getToolTipText(Object element) {
+               try {
+                       if (element instanceof Node) {
+                               return getToolTipText((Node) element);
+                       } else if (element instanceof WrappedNode) {
+                               return getToolTipText(((WrappedNode) element).getNode());
+                       } else if (element instanceof NodesWrapper) {
+                               return getToolTipText(((NodesWrapper) element).getNode());
+                       }
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot get tooltip for " + element, e);
+               }
+               return super.getToolTipText(element);
+       }
+
+       protected String getToolTipText(Node node) throws RepositoryException {
+               return null;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrImages.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrImages.java
new file mode 100644 (file)
index 0000000..255ea7a
--- /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.eclipse.ui.jcr;
+
+import org.eclipse.swt.graphics.Image;
+
+/** Shared icons. */
+public class JcrImages {
+       public final static Image NODE = JcrUiPlugin.getImageDescriptor(
+                       "icons/node.gif").createImage();
+       public final static Image FOLDER = JcrUiPlugin.getImageDescriptor(
+                       "icons/folder.gif").createImage();
+       public final static Image FILE = JcrUiPlugin.getImageDescriptor(
+                       "icons/file.gif").createImage();
+       public final static Image BINARY = JcrUiPlugin.getImageDescriptor(
+                       "icons/binary.png").createImage();
+       public final static Image HOME = JcrUiPlugin.getImageDescriptor(
+                       "icons/home.gif").createImage();
+       public final static Image SORT = JcrUiPlugin.getImageDescriptor(
+                       "icons/sort.gif").createImage();
+
+       public final static Image REPOSITORIES = JcrUiPlugin.getImageDescriptor(
+                       "icons/repositories.gif").createImage();
+       public final static Image REPOSITORY_DISCONNECTED = JcrUiPlugin
+                       .getImageDescriptor("icons/repository_disconnected.gif")
+                       .createImage();
+       public final static Image REPOSITORY_CONNECTED = JcrUiPlugin
+                       .getImageDescriptor("icons/repository_connected.gif").createImage();
+       public final static Image REMOTE_DISCONNECTED = JcrUiPlugin
+                       .getImageDescriptor("icons/remote_disconnected.gif").createImage();
+       public final static Image REMOTE_CONNECTED = JcrUiPlugin
+                       .getImageDescriptor("icons/remote_connected.gif").createImage();
+       public final static Image WORKSPACE_DISCONNECTED = JcrUiPlugin
+                       .getImageDescriptor("icons/workspace_disconnected.png")
+                       .createImage();
+       public final static Image WORKSPACE_CONNECTED = JcrUiPlugin
+                       .getImageDescriptor("icons/workspace_connected.png").createImage();
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrPreferenceStore.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrPreferenceStore.java
new file mode 100644 (file)
index 0000000..c9777ce
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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.eclipse.ui.jcr;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.version.VersionManager;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+import org.eclipse.jface.preference.PreferenceStore;
+import org.eclipse.ui.preferences.ScopedPreferenceStore;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Persist preferences as key/value pairs under ~/argeo:preferences.<br>
+ * TODO: better integrate JCR and Eclipse:<br>
+ * - typing<br>
+ * - use eclipse preferences<br>
+ * - better integrate with {@link ScopedPreferenceStore} provided by RAP
+ */
+public class JcrPreferenceStore extends PreferenceStore implements ArgeoNames {
+       private Session session;
+       private BundleContext bundleContext;
+
+       /** Retrieves the preference node */
+       protected Node getPreferenceNode() {
+               try {
+                       if (session.hasPendingChanges())
+                               session.save();
+                       Node userHome = UserJcrUtils.getUserHome(session);
+                       if (userHome == null)
+                               throw new ArgeoException("No user home for "
+                                               + session.getUserID());
+                       Node preferences;
+                       if (!userHome.hasNode(ARGEO_PREFERENCES)) {
+                               preferences = userHome.addNode(ARGEO_PREFERENCES);
+                               preferences.addMixin(ArgeoTypes.ARGEO_PREFERENCE_NODE);
+                               session.save();
+                       } else
+                               preferences = userHome.getNode(ARGEO_PREFERENCES);
+
+                       String pluginPreferencesName = bundleContext.getBundle()
+                                       .getSymbolicName();
+                       Node pluginPreferences;
+                       if (!preferences.hasNode(pluginPreferencesName)) {
+                               VersionManager vm = session.getWorkspace().getVersionManager();
+                               vm.checkout(preferences.getPath());
+                               pluginPreferences = preferences.addNode(pluginPreferencesName);
+                               pluginPreferences.addMixin(ArgeoTypes.ARGEO_PREFERENCE_NODE);
+                               session.save();
+                               vm.checkin(preferences.getPath());
+                       } else
+                               pluginPreferences = preferences.getNode(pluginPreferencesName);
+                       return pluginPreferences;
+               } catch (RepositoryException e) {
+                       e.printStackTrace();
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoException("Cannot retrieve preferences", e);
+               }
+
+       }
+
+       @Override
+       public void load() throws IOException {
+               ByteArrayOutputStream out = null;
+               ByteArrayInputStream in = null;
+               try {
+                       Properties props = new Properties();
+                       PropertyIterator it = getPreferenceNode().getProperties();
+                       while (it.hasNext()) {
+                               Property p = it.nextProperty();
+                               if (!p.isMultiple() && !p.getDefinition().isProtected()) {
+                                       props.setProperty(p.getName(), p.getValue().getString());
+                               }
+                       }
+                       out = new ByteArrayOutputStream();
+                       props.store(out, "");
+                       in = new ByteArrayInputStream(out.toByteArray());
+                       load(in);
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       throw new ArgeoException("Cannot load preferences", e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+                       IOUtils.closeQuietly(out);
+               }
+       }
+
+       @Override
+       public void save() throws IOException {
+               ByteArrayOutputStream out = null;
+               ByteArrayInputStream in = null;
+               Node pluginPreferences = null;
+               try {
+                       out = new ByteArrayOutputStream();
+                       save(out, "");
+                       in = new ByteArrayInputStream(out.toByteArray());
+                       Properties props = new Properties();
+                       props.load(in);
+                       pluginPreferences = getPreferenceNode();
+                       VersionManager vm = pluginPreferences.getSession().getWorkspace()
+                                       .getVersionManager();
+                       vm.checkout(pluginPreferences.getPath());
+                       for (Object key : props.keySet()) {
+                               String name = key.toString();
+                               String value = props.getProperty(name);
+                               pluginPreferences.setProperty(name, value);
+                       }
+                       JcrUtils.updateLastModified(pluginPreferences);
+                       pluginPreferences.getSession().save();
+                       vm.checkin(pluginPreferences.getPath());
+               } catch (Exception e) {
+                       JcrUtils.discardUnderlyingSessionQuietly(pluginPreferences);
+                       throw new ArgeoException("Cannot save preferences", e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+                       IOUtils.closeQuietly(out);
+               }
+       }
+
+       public void init() {
+               try {
+                       load();
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot initialize preference store", e);
+               }
+       }
+
+       public void setSession(Session session) {
+               this.session = session;
+       }
+
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrUiPlugin.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrUiPlugin.java
new file mode 100644 (file)
index 0000000..adfa1a4
--- /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.eclipse.ui.jcr;
+
+import java.util.ResourceBundle;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+public class JcrUiPlugin extends AbstractUIPlugin {
+       private final static Log log = LogFactory.getLog(JcrUiPlugin.class);
+
+       public final static String ID = "org.argeo.eclipse.ui.jcr";
+
+       private ResourceBundle messages;
+
+       private static JcrUiPlugin plugin;
+
+       public void start(BundleContext context) throws Exception {
+               super.start(context);
+               plugin = this;
+               messages = ResourceBundle.getBundle("org.argeo.eclipse.ui.jcr");
+       }
+
+       public static JcrUiPlugin getDefault() {
+               return plugin;
+       }
+
+       public static ImageDescriptor getImageDescriptor(String path) {
+               return imageDescriptorFromPlugin(ID, path);
+       }
+
+       /** Returns the internationalized label for the given key */
+       public static String getMessage(String key) {
+               try {
+                       return getDefault().messages.getString(key);
+               } catch (NullPointerException npe) {
+                       log.warn(key + " not found.");
+                       return key;
+               }
+       }
+
+       /**
+        * Gives access to the internationalization message bundle. Returns null in
+        * case the ClientUiPlugin is not started (for JUnit tests, by instance)
+        */
+       public static ResourceBundle getMessagesBundle() {
+               if (getDefault() != null)
+                       // To avoid NPE
+                       return getDefault().messages;
+               else
+                       return null;
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrUiUtils.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/JcrUiUtils.java
new file mode 100644 (file)
index 0000000..8ee13aa
--- /dev/null
@@ -0,0 +1,146 @@
+package org.argeo.eclipse.ui.jcr;
+
+import java.util.Calendar;
+
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.jcr.lists.NodeViewerComparator;
+import org.argeo.eclipse.ui.jcr.lists.RowViewerComparator;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Table;
+
+/** Utility methods to simplify UI development using eclipse and JCR. */
+public class JcrUiUtils {
+
+       /**
+        * Centralizes management of updating property value. Among other to avoid
+        * infinite loop when the new value is the same as the ones that is already
+        * stored in JCR.
+        * 
+        * @return true if the value as changed
+        */
+       public static boolean setJcrProperty(Node node, String propName,
+                       int propertyType, Object value) {
+               try {
+                       // int propertyType = getPic().getProperty(propName).getType();
+                       switch (propertyType) {
+                       case PropertyType.STRING:
+                               if ("".equals((String) value)
+                                               && (!node.hasProperty(propName) || node
+                                                               .hasProperty(propName)
+                                                               && "".equals(node.getProperty(propName)
+                                                                               .getString())))
+                                       // workaround the fact that the Text widget value cannot be
+                                       // set to null
+                                       return false;
+                               else if (node.hasProperty(propName)
+                                               && node.getProperty(propName).getString()
+                                                               .equals((String) value))
+                                       // nothing changed yet
+                                       return false;
+                               else {
+                                       node.setProperty(propName, (String) value);
+                                       return true;
+                               }
+                       case PropertyType.BOOLEAN:
+                               if (node.hasProperty(propName)
+                                               && node.getProperty(propName).getBoolean() == (Boolean) value)
+                                       // nothing changed yet
+                                       return false;
+                               else {
+                                       node.setProperty(propName, (Boolean) value);
+                                       return true;
+                               }
+                       case PropertyType.DATE:
+                               if (node.hasProperty(propName)
+                                               && node.getProperty(propName).getDate()
+                                                               .equals((Calendar) value))
+                                       // nothing changed yet
+                                       return false;
+                               else {
+                                       node.setProperty(propName, (Calendar) value);
+                                       return true;
+                               }
+                       case PropertyType.LONG:
+                               Long lgValue = (Long) value;
+
+                               if (lgValue == null)
+                                       lgValue = 0L;
+
+                               if (node.hasProperty(propName)
+                                               && node.getProperty(propName).getLong() == lgValue)
+                                       // nothing changed yet
+                                       return false;
+                               else {
+                                       node.setProperty(propName, lgValue);
+                                       return true;
+                               }
+
+                       default:
+                               throw new ArgeoException("Unimplemented property save");
+                       }
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Unexpected error while setting property",
+                                       re);
+               }
+       }
+
+       /**
+        * Creates a new selection adapter in order to provide sorting abitily on a
+        * swt table that display a row list
+        **/
+       public static SelectionAdapter getRowSelectionAdapter(final int index,
+                       final int propertyType, final String selectorName,
+                       final String propertyName, final RowViewerComparator comparator,
+                       final TableViewer viewer) {
+               SelectionAdapter selectionAdapter = new SelectionAdapter() {
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               Table table = viewer.getTable();
+                               comparator.setColumn(propertyType, selectorName, propertyName);
+                               int dir = table.getSortDirection();
+                               if (table.getSortColumn() == table.getColumn(index)) {
+                                       dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
+                               } else {
+                                       dir = SWT.DOWN;
+                               }
+                               table.setSortDirection(dir);
+                               table.setSortColumn(table.getColumn(index));
+                               viewer.refresh();
+                       }
+               };
+               return selectionAdapter;
+       }
+
+       /**
+        * Creates a new selection adapter in order to provide sorting abitily on a
+        * swt table that display a row list
+        **/
+       public static SelectionAdapter getNodeSelectionAdapter(final int index,
+                       final int propertyType, final String propertyName,
+                       final NodeViewerComparator comparator, final TableViewer viewer) {
+               SelectionAdapter selectionAdapter = new SelectionAdapter() {
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               Table table = viewer.getTable();
+                               comparator.setColumn(propertyType, propertyName);
+                               int dir = table.getSortDirection();
+                               if (table.getSortColumn() == table.getColumn(index)) {
+                                       dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
+                               } else {
+                                       dir = SWT.DOWN;
+                               }
+                               table.setSortDirection(dir);
+                               table.setSortColumn(table.getColumn(index));
+                               viewer.refresh();
+                       }
+               };
+               return selectionAdapter;
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/NodeElementComparer.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/NodeElementComparer.java
new file mode 100644 (file)
index 0000000..f284b9c
--- /dev/null
@@ -0,0 +1,51 @@
+/*\r
+ * Copyright (C) 2007-2012 Argeo GmbH\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *         http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.argeo.eclipse.ui.jcr;\r
+\r
+import javax.jcr.Node;\r
+import javax.jcr.RepositoryException;\r
+\r
+import org.argeo.ArgeoException;\r
+import org.eclipse.jface.viewers.IElementComparer;\r
+\r
+/** Element comparer for JCR node, to be used in JFace viewers. */\r
+public class NodeElementComparer implements IElementComparer {\r
+\r
+       public boolean equals(Object a, Object b) {\r
+               try {\r
+                       if ((a instanceof Node) && (b instanceof Node)) {\r
+                               Node nodeA = (Node) a;\r
+                               Node nodeB = (Node) b;\r
+                               return nodeA.getIdentifier().equals(nodeB.getIdentifier());\r
+                       } else {\r
+                               return a.equals(b);\r
+                       }\r
+               } catch (RepositoryException e) {\r
+                       throw new ArgeoException("Cannot compare nodes", e);\r
+               }\r
+       }\r
+\r
+       public int hashCode(Object element) {\r
+               try {\r
+                       if (element instanceof Node)\r
+                               return ((Node) element).getIdentifier().hashCode();\r
+                       return element.hashCode();\r
+               } catch (RepositoryException e) {\r
+                       throw new ArgeoException("Cannot get hash code", e);\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/NodesWrapper.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/NodesWrapper.java
new file mode 100644 (file)
index 0000000..dcd3b42
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.eclipse.ui.jcr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+
+/**
+ * Element of tree which is based on a node, but whose children are not
+ * necessarily this node children.
+ */
+public class NodesWrapper {
+       private final Node node;
+
+       public NodesWrapper(Node node) {
+               super();
+               this.node = node;
+       }
+
+       protected NodeIterator getNodeIterator() throws RepositoryException {
+               return node.getNodes();
+       }
+
+       protected List<WrappedNode> getWrappedNodes() throws RepositoryException {
+               List<WrappedNode> nodes = new ArrayList<WrappedNode>();
+               for (NodeIterator nit = getNodeIterator(); nit.hasNext();)
+                       nodes.add(new WrappedNode(this, nit.nextNode()));
+               return nodes;
+       }
+
+       public Object[] getChildren() {
+               try {
+                       return getWrappedNodes().toArray();
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot get wrapped children", e);
+               }
+       }
+
+       /**
+        * @return true by default because we don't want to compute the wrapped
+        *         nodes twice
+        */
+       public Boolean hasChildren() {
+               return true;
+       }
+
+       public Node getNode() {
+               return node;
+       }
+
+       @Override
+       public int hashCode() {
+               return node.hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (obj instanceof NodesWrapper)
+                       return node.equals(((NodesWrapper) obj).getNode());
+               else
+                       return false;
+       }
+
+       public String toString() {
+               return "nodes wrapper based on " + node;
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java
new file mode 100644 (file)
index 0000000..0dad19c
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.eclipse.ui.jcr;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+
+/** Simple JCR node content provider taking a list of String as base path. */
+public class SimpleNodeContentProvider extends AbstractNodeContentProvider {
+       private final List<String> basePaths;
+       private Boolean mkdirs = false;
+
+       public SimpleNodeContentProvider(Session session, String... basePaths) {
+               this(session, Arrays.asList(basePaths));
+       }
+
+       public SimpleNodeContentProvider(Session session, List<String> basePaths) {
+               super(session);
+               this.basePaths = basePaths;
+       }
+
+       @Override
+       protected Boolean isBasePath(String path) {
+               if (basePaths.contains(path))
+                       return true;
+               return super.isBasePath(path);
+       }
+
+       public Object[] getElements(Object inputElement) {
+               try {
+                       List<Node> baseNodes = new ArrayList<Node>();
+                       for (String basePath : basePaths)
+                               if (mkdirs && !getSession().itemExists(basePath))
+                                       baseNodes.add(JcrUtils.mkdirs(getSession(), basePath));
+                               else
+                                       baseNodes.add(getSession().getNode(basePath));
+                       return baseNodes.toArray();
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot get base nodes for " + basePaths,
+                                       e);
+               }
+       }
+
+       public List<String> getBasePaths() {
+               return basePaths;
+       }
+
+       public void setMkdirs(Boolean mkdirs) {
+               this.mkdirs = mkdirs;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/WrappedNode.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/WrappedNode.java
new file mode 100644 (file)
index 0000000..c0e1973
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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.eclipse.ui.jcr;
+
+import javax.jcr.Node;
+
+/** Wraps a node (created from a {@link NodesWrapper}) */
+public class WrappedNode {
+       private final NodesWrapper parent;
+       private final Node node;
+
+       public WrappedNode(NodesWrapper parent, Node node) {
+               super();
+               this.parent = parent;
+               this.node = node;
+       }
+
+       public NodesWrapper getParent() {
+               return parent;
+       }
+
+       public Node getNode() {
+               return node;
+       }
+
+       public String toString() {
+               return "wrapped " + node;
+       }
+
+       @Override
+       public int hashCode() {
+               return node.hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (obj instanceof WrappedNode)
+                       return node.equals(((WrappedNode) obj).getNode());
+               else
+                       return false;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/AddFileFolder.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/AddFileFolder.java
new file mode 100644 (file)
index 0000000..5fc7db0
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.eclipse.ui.jcr.commands;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.eclipse.ui.dialogs.SingleValue;
+import org.argeo.eclipse.ui.jcr.JcrUiPlugin;
+import org.argeo.eclipse.ui.jcr.views.AbstractJcrBrowser;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/** Adds a node of type nt:folder */
+public class AddFileFolder extends AbstractHandler {
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage().getSelection();
+               AbstractJcrBrowser view = (AbstractJcrBrowser) HandlerUtil
+                               .getActiveWorkbenchWindow(event).getActivePage()
+                               .findView(HandlerUtil.getActivePartId(event));
+               if (selection != null && !selection.isEmpty()
+                               && selection instanceof IStructuredSelection) {
+                       Object obj = ((IStructuredSelection) selection).getFirstElement();
+
+                       if (obj instanceof Node) {
+                               String folderName = SingleValue.ask("Folder name",
+                                               "Enter folder name");
+                               if (folderName != null) {
+                                       Node parentNode = (Node) obj;
+                                       try {
+                                               Node newNode = parentNode.addNode(folderName,
+                                                               NodeType.NT_FOLDER);
+                                               view.nodeAdded(parentNode, newNode);
+                                               parentNode.getSession().save();
+                                       } catch (RepositoryException e) {
+                                               ErrorFeedback.show("Cannot create folder " + folderName
+                                                               + " under " + parentNode, e);
+                                       }
+                               }
+                       } else {
+                               ErrorFeedback.show(JcrUiPlugin
+                                               .getMessage("errorUnvalidNtFolderNodeType"));
+                       }
+               }
+               return null;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/DeleteNodes.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/DeleteNodes.java
new file mode 100644 (file)
index 0000000..1686748
--- /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.eclipse.ui.jcr.commands;
+
+import java.util.Iterator;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.eclipse.ui.jcr.views.AbstractJcrBrowser;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * Deletes the selected nodes and refresh the corresponding AbstractJcrView.
+ * Note that no model specific check is done to see if the node can be removed
+ * or not. Extend or override to implement specific behaviour.
+ */
+public class DeleteNodes extends AbstractHandler {
+       public final static String ID = "org.argeo.eclipse.ui.jcr.deleteNodes";
+       public final static String DEFAULT_LABEL = "Delete selected nodes";
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage().getSelection();
+               AbstractJcrBrowser view = (AbstractJcrBrowser) HandlerUtil
+                               .getActiveWorkbenchWindow(event).getActivePage()
+                               .findView(HandlerUtil.getActivePartId(event));
+
+               if (selection != null && selection instanceof IStructuredSelection) {
+                       Iterator<?> it = ((IStructuredSelection) selection).iterator();
+                       Object obj = null;
+                       Node ancestor = null;
+                       try {
+                               while (it.hasNext()) {
+                                       obj = it.next();
+                                       if (obj instanceof Node) {
+                                               Node node = (Node) obj;
+                                               Node parentNode = node.getParent();
+                                               node.remove();
+                                               node.getSession().save();
+                                               ancestor = getOlder(ancestor, parentNode);
+                                       }
+                               }
+                               if (ancestor != null)
+                                       view.nodeRemoved(ancestor);
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Cannot delete node " + obj, e);
+                       }
+               }
+               return null;
+       }
+
+       protected Node getOlder(Node A, Node B) {
+               try {
+
+                       if (A == null)
+                               return B == null ? null : B;
+                       // Todo enhanced this method
+                       else
+                               return A.getDepth() <= B.getDepth() ? A : B;
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Cannot find ancestor", re);
+               }
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/OpenGenericJcrQueryEditor.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/OpenGenericJcrQueryEditor.java
new file mode 100644 (file)
index 0000000..ce42266
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.eclipse.ui.jcr.commands;
+
+import org.argeo.eclipse.ui.jcr.editors.JcrQueryEditorInput;
+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.handlers.HandlerUtil;
+
+/** Open a JCR query editor. */
+public class OpenGenericJcrQueryEditor extends AbstractHandler {
+       private String editorId;
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               try {
+                       JcrQueryEditorInput editorInput = new JcrQueryEditorInput("", null);
+                       IWorkbenchPage activePage = HandlerUtil.getActiveWorkbenchWindow(
+                                       event).getActivePage();
+                       activePage.openEditor(editorInput, editorId);
+               } catch (Exception e) {
+                       throw new ExecutionException("Cannot open editor", e);
+               }
+               return null;
+       }
+
+       public void setEditorId(String editorId) {
+               this.editorId = editorId;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/Refresh.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/Refresh.java
new file mode 100644 (file)
index 0000000..f62be37
--- /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.eclipse.ui.jcr.commands;
+
+import java.util.Iterator;
+
+import org.argeo.eclipse.ui.jcr.views.AbstractJcrBrowser;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * Call the refresh method of the active AbstractJcrBrowser instance.
+ * 
+ * Warning: this method only refreshes the viewer, if the model is "stale", e.g.
+ * if some changes in the underlying data have not yet been propagated to the
+ * model, the view will not display up-to-date information.
+ */
+@Deprecated
+public class Refresh extends AbstractHandler {
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage().getSelection();
+               AbstractJcrBrowser view = (AbstractJcrBrowser) HandlerUtil
+                               .getActiveWorkbenchWindow(event).getActivePage()
+                               .findView(HandlerUtil.getActivePartId(event));
+               if (selection != null && selection instanceof IStructuredSelection) {
+                       Iterator<?> it = ((IStructuredSelection) selection).iterator();
+                       while (it.hasNext()) {
+                               Object obj = it.next();
+                               view.refresh(obj);
+                       }
+               }
+               return null;
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/editors/AbstractJcrQueryEditor.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/editors/AbstractJcrQueryEditor.java
new file mode 100644 (file)
index 0000000..5e7d696
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * 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.eclipse.ui.jcr.editors;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.Row;
+import javax.jcr.query.RowIterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.GenericTableComparator;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.part.EditorPart;
+
+/** Executes any JCR query. */
+public abstract class AbstractJcrQueryEditor extends EditorPart {
+       private final static Log log = LogFactory
+                       .getLog(AbstractJcrQueryEditor.class);
+
+       protected String initialQuery;
+       protected String initialQueryType;
+
+       /* DEPENDENCY INJECTION */
+       private Session session;
+
+       // Widgets
+       private TableViewer viewer;
+       private List<TableViewerColumn> tableViewerColumns = new ArrayList<TableViewerColumn>();
+       private GenericTableComparator comparator;
+
+       /** Override to layout a form enabling the end user to build his query */
+       protected abstract void createQueryForm(Composite parent);
+
+       @Override
+       public void init(IEditorSite site, IEditorInput input)
+                       throws PartInitException {
+               JcrQueryEditorInput editorInput = (JcrQueryEditorInput) input;
+               initialQuery = editorInput.getQuery();
+               initialQueryType = editorInput.getQueryType();
+               setSite(site);
+               setInput(editorInput);
+       }
+
+       @Override
+       public final void createPartControl(final Composite parent) {
+               parent.setLayout(new FillLayout());
+
+               SashForm sashForm = new SashForm(parent, SWT.VERTICAL);
+               sashForm.setSashWidth(4);
+               sashForm.setLayout(new FillLayout());
+
+               Composite top = new Composite(sashForm, SWT.NONE);
+               GridLayout gl = new GridLayout(1, false);
+               top.setLayout(gl);
+
+               createQueryForm(top);
+
+               Composite bottom = new Composite(sashForm, SWT.NONE);
+               bottom.setLayout(new GridLayout(1, false));
+               sashForm.setWeights(getWeights());
+
+               viewer = new TableViewer(bottom);
+               viewer.getTable().setLayoutData(
+                               new GridData(SWT.FILL, SWT.FILL, true, true));
+               viewer.getTable().setHeaderVisible(true);
+               viewer.setContentProvider(getQueryResultContentProvider());
+               viewer.setInput(getEditorSite());
+
+               if (getComparator() != null) {
+                       comparator = getComparator();
+                       viewer.setComparator(comparator);
+               }
+               if (getTableDoubleClickListener() != null)
+                       viewer.addDoubleClickListener(getTableDoubleClickListener());
+
+       }
+
+       protected void executeQuery(String statement) {
+               try {
+                       if (log.isDebugEnabled())
+                               log.debug("Query : " + statement);
+
+                       QueryResult qr = session.getWorkspace().getQueryManager()
+                                       .createQuery(statement, initialQueryType).execute();
+
+                       // remove previous columns
+                       for (TableViewerColumn tvc : tableViewerColumns)
+                               tvc.getColumn().dispose();
+
+                       int i = 0;
+                       for (final String columnName : qr.getColumnNames()) {
+                               TableViewerColumn tvc = new TableViewerColumn(viewer, SWT.NONE);
+                               configureColumn(columnName, tvc, i);
+                               tvc.setLabelProvider(getLabelProvider(columnName));
+                               tableViewerColumns.add(tvc);
+                               i++;
+                       }
+
+                       // Must create a local list: QueryResults can only be read once.
+                       try {
+                               List<Row> rows = new ArrayList<Row>();
+                               RowIterator rit = qr.getRows();
+                               while (rit.hasNext()) {
+                                       rows.add(rit.nextRow());
+                               }
+                               viewer.setInput(rows);
+                       } catch (RepositoryException e) {
+                               throw new ArgeoException("Cannot read query result", e);
+                       }
+
+               } catch (RepositoryException e) {
+                       ErrorDialog.openError(null, "Error", "Cannot execute JCR query: "
+                                       + statement, new Status(IStatus.ERROR,
+                                       "org.argeo.eclipse.ui.jcr", e.getMessage()));
+               }
+       }
+
+       /**
+        * To be overidden to adapt size of form and result frames.
+        * 
+        * @return
+        */
+       protected int[] getWeights() {
+               return new int[] { 30, 70 };
+       }
+
+       /**
+        * To be overidden to implement a doubleclick Listener on one of the rows of
+        * the table.
+        * 
+        * @return
+        */
+       protected IDoubleClickListener getTableDoubleClickListener() {
+               return null;
+       }
+
+       /**
+        * To be overiden in order to implement a specific
+        * QueryResultContentProvider
+        */
+       protected IStructuredContentProvider getQueryResultContentProvider() {
+               return new QueryResultContentProvider();
+       }
+
+       /**
+        * Enable specific implementation for columns
+        */
+       protected List<TableViewerColumn> getTableViewerColumns() {
+               return tableViewerColumns;
+       }
+
+       /**
+        * Enable specific implementation for columns
+        */
+       protected TableViewer getTableViewer() {
+               return viewer;
+       }
+
+       /**
+        * To be overridden in order to configure column label providers .
+        */
+       protected ColumnLabelProvider getLabelProvider(final String columnName) {
+               return new ColumnLabelProvider() {
+                       public String getText(Object element) {
+                               Row row = (Row) element;
+                               try {
+                                       return row.getValue(columnName).getString();
+                               } catch (RepositoryException e) {
+                                       throw new ArgeoException("Cannot display row " + row, e);
+                               }
+                       }
+
+                       public Image getImage(Object element) {
+                               return null;
+                       }
+               };
+       }
+
+       /**
+        * To be overridden in order to configure the columns.
+        * 
+        * @deprecated use {@link
+        *             org.argeo.eclipse.ui.jcr.editors.AbstractJcrQueryEditor.
+        *             configureColumn(String jcrColumnName, TableViewerColumn
+        *             column, int columnIndex)} instead
+        */
+       protected void configureColumn(String jcrColumnName,
+                       TableViewerColumn column) {
+               column.getColumn().setWidth(50);
+               column.getColumn().setText(jcrColumnName);
+       }
+
+       /** To be overridden in order to configure the columns. */
+       protected void configureColumn(String jcrColumnName,
+                       TableViewerColumn column, int columnIndex) {
+               column.getColumn().setWidth(50);
+               column.getColumn().setText(jcrColumnName);
+       }
+
+       private class QueryResultContentProvider implements
+                       IStructuredContentProvider {
+
+               public void dispose() {
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+
+               public Object[] getElements(Object inputElement) {
+
+                       if (inputElement instanceof List)
+                               return ((List<?>) inputElement).toArray();
+
+                       // Never reached might be deleted in future release
+                       if (!(inputElement instanceof QueryResult))
+                               return new String[] {};
+
+                       try {
+                               QueryResult queryResult = (QueryResult) inputElement;
+                               List<Row> rows = new ArrayList<Row>();
+                               RowIterator rit = queryResult.getRows();
+                               while (rit.hasNext()) {
+                                       rows.add(rit.nextRow());
+                               }
+
+                               // List<Node> elems = new ArrayList<Node>();
+                               // NodeIterator nit = queryResult.getNodes();
+                               // while (nit.hasNext()) {
+                               // elems.add(nit.nextNode());
+                               // }
+                               return rows.toArray();
+                       } catch (RepositoryException e) {
+                               throw new ArgeoException("Cannot read query result", e);
+                       }
+               }
+
+       }
+
+       /**
+        * Might be used by children classes to sort columns.
+        * 
+        * @param column
+        * @param index
+        * @return
+        */
+       protected SelectionAdapter getSelectionAdapter(final TableColumn column,
+                       final int index) {
+
+               // A comparator must be define
+               if (comparator == null)
+                       return null;
+
+               SelectionAdapter selectionAdapter = new SelectionAdapter() {
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+
+                               try {
+
+                                       comparator.setColumn(index);
+                                       int dir = viewer.getTable().getSortDirection();
+                                       if (viewer.getTable().getSortColumn() == column) {
+                                               dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
+                                       } else {
+
+                                               dir = SWT.DOWN;
+                                       }
+                                       viewer.getTable().setSortDirection(dir);
+                                       viewer.getTable().setSortColumn(column);
+                                       viewer.refresh();
+                               } catch (Exception exc) {
+                                       exc.printStackTrace();
+                               }
+                       }
+               };
+               return selectionAdapter;
+       }
+
+       /**
+        * To be overridden to enable sorting.
+        */
+       protected GenericTableComparator getComparator() {
+               return null;
+       }
+
+       @Override
+       public boolean isDirty() {
+               return false;
+       }
+
+       @Override
+       public void doSave(IProgressMonitor monitor) {
+               // TODO save the query in JCR?
+       }
+
+       @Override
+       public void doSaveAs() {
+       }
+
+       @Override
+       public boolean isSaveAsAllowed() {
+               return false;
+       }
+
+       /** Returns the injected current session */
+       protected Session getSession() {
+               return session;
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setSession(Session session) {
+               this.session = session;
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/editors/JcrQueryEditorInput.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/editors/JcrQueryEditorInput.java
new file mode 100644 (file)
index 0000000..eedccc8
--- /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.eclipse.ui.jcr.editors;
+
+import javax.jcr.query.Query;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IPersistableElement;
+
+public class JcrQueryEditorInput implements IEditorInput {
+       private final String query;
+       private final String queryType;
+
+       public JcrQueryEditorInput(String query, String queryType) {
+               this.query = query;
+               if (queryType == null)
+                       this.queryType = Query.JCR_SQL2;
+               else
+                       this.queryType = queryType;
+       }
+
+       public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+               return null;
+       }
+
+       public boolean exists() {
+               return true;
+       }
+
+       public ImageDescriptor getImageDescriptor() {
+               return null;
+       }
+
+       public String getName() {
+               return query;
+       }
+
+       public IPersistableElement getPersistable() {
+               return null;
+       }
+
+       public String getToolTipText() {
+               return query;
+       }
+
+       public String getQuery() {
+               return query;
+       }
+
+       public String getQueryType() {
+               return queryType;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/editors/NodeEditorInput.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/editors/NodeEditorInput.java
new file mode 100644 (file)
index 0000000..a55884f
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.eclipse.ui.jcr.editors;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IPersistableElement;
+
+/**
+ * A canonical editor input based on a path to a node. In a multirepository
+ * environment, path can be enriched with Repository Alias and workspace
+ */
+
+public class NodeEditorInput implements IEditorInput {
+       private final String path;
+
+       public NodeEditorInput(String path) {
+               this.path = path;
+       }
+
+       public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+               return null;
+       }
+
+       public boolean exists() {
+               return true;
+       }
+
+       public ImageDescriptor getImageDescriptor() {
+               return null;
+       }
+
+       public String getName() {
+               return path;
+       }
+
+       public IPersistableElement getPersistable() {
+               return null;
+       }
+
+       public String getToolTipText() {
+               return path;
+       }
+
+       public String getPath() {
+               return path;
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/ColumnDefinition.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/ColumnDefinition.java
new file mode 100644 (file)
index 0000000..9e33827
--- /dev/null
@@ -0,0 +1,88 @@
+package org.argeo.eclipse.ui.jcr.lists;
+
+/**
+ * Utility object to manage column in various tables and extracts displaying
+ * data from JCR
+ */
+public class ColumnDefinition {
+       private final static int DEFAULT_COLUMN_SIZE = 120;
+
+       private String selectorName;
+       private String propertyName;
+       private String headerLabel;
+       private int propertyType;
+       private int columnSize = DEFAULT_COLUMN_SIZE;
+
+       /**
+        * new column using default width
+        * 
+        * @param selectorName
+        * @param propertyName
+        * @param propertyType
+        * @param headerLabel
+        */
+       public ColumnDefinition(String selectorName, String propertyName,
+                       int propertyType, String headerLabel) {
+               this.selectorName = selectorName;
+               this.propertyName = propertyName;
+               this.propertyType = propertyType;
+               this.headerLabel = headerLabel;
+       }
+
+       /**
+        * 
+        * @param selectorName
+        * @param propertyName
+        * @param propertyType
+        * @param headerLabel
+        * @param columnSize
+        */
+       public ColumnDefinition(String selectorName, String propertyName,
+                       int propertyType, String headerLabel, int columnSize) {
+               this.selectorName = selectorName;
+               this.propertyName = propertyName;
+               this.propertyType = propertyType;
+               this.headerLabel = headerLabel;
+               this.columnSize = columnSize;
+       }
+
+       public String getSelectorName() {
+               return selectorName;
+       }
+
+       public void setSelectorName(String selectorName) {
+               this.selectorName = selectorName;
+       }
+
+       public String getPropertyName() {
+               return propertyName;
+       }
+
+       public void setPropertyName(String propertyName) {
+               this.propertyName = propertyName;
+       }
+
+       public String getHeaderLabel() {
+               return headerLabel;
+       }
+
+       public void setHeaderLabel(String headerLabel) {
+               this.headerLabel = headerLabel;
+       }
+
+       public int getPropertyType() {
+               return propertyType;
+       }
+
+       public void setPropertyType(int propertyType) {
+               this.propertyType = propertyType;
+       }
+
+       public int getColumnSize() {
+               return columnSize;
+       }
+
+       public void setColumnSize(int columnSize) {
+               this.columnSize = columnSize;
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/IListProvider.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/IListProvider.java
new file mode 100644 (file)
index 0000000..622e2e2
--- /dev/null
@@ -0,0 +1,20 @@
+package org.argeo.eclipse.ui.jcr.lists;
+
+import java.util.List;
+
+/**
+ * Views and editors can implement this interface so that one of the row list
+ * that is displayed in the part (For instance in a Table or a Tree Viewer) can
+ * be rebuilt externally. typically to generate csv or calc extract.
+ */
+public interface IListProvider {
+       /**
+        * Returns an array of current and relevant elements
+        */
+       public Object[] getElements(String extractId);
+
+       /**
+        * Returns the column definition for passed ID
+        */
+       public List<ColumnDefinition> getColumnDefinition(String extractId);
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java
new file mode 100644 (file)
index 0000000..11f12e6
--- /dev/null
@@ -0,0 +1,192 @@
+package org.argeo.eclipse.ui.jcr.lists;
+
+import java.math.BigDecimal;
+import java.util.Calendar;
+
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+
+import org.argeo.ArgeoException;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+
+/**
+ * Base comparator to enable ordering on Table or Tree viewer that display Jcr
+ * Nodes.
+ * 
+ * Note that the following snippet must be added before setting the comparator
+ * to the corresponding control: <code>
+ * // IMPORTANT: initialize comparator before setting it
+ * ColumnDefinition firstCol = colDefs.get(0);
+ * comparator.setColumn(firstCol.getPropertyType(),
+ * firstCol.getPropertyName());
+ * viewer.setComparator(comparator); </code>
+ */
+public class NodeViewerComparator extends ViewerComparator {
+
+       protected String propertyName;
+
+       protected int propertyType;
+       public static final int ASCENDING = 0, DESCENDING = 1;
+       protected int direction = DESCENDING;
+
+       public NodeViewerComparator() {
+       }
+
+       /**
+        * e1 and e2 must both be Jcr nodes.
+        * 
+        * @param viewer
+        * @param e1
+        * @param e2
+        * @return
+        */
+       @Override
+       public int compare(Viewer viewer, Object e1, Object e2) {
+               int rc = 0;
+               long lc = 0;
+
+               try {
+
+                       Node n1 = (Node) e1;
+                       Node n2 = (Node) e2;
+
+                       Value v1 = null;
+                       Value v2 = null;
+                       if (n1.hasProperty(propertyName))
+                               v1 = n1.getProperty(propertyName).getValue();
+                       if (n2.hasProperty(propertyName))
+                               v2 = n2.getProperty(propertyName).getValue();
+
+                       if (v2 == null && v1 == null)
+                               return 0;
+                       else if (v2 == null)
+                               return -1;
+                       else if (v1 == null)
+                               return 1;
+
+                       switch (propertyType) {
+                       case PropertyType.STRING:
+                               rc = v1.getString().compareTo(v2.getString());
+                               break;
+                       case PropertyType.BOOLEAN:
+                               boolean b1 = v1.getBoolean();
+                               boolean b2 = v2.getBoolean();
+                               if (b1 == b2)
+                                       rc = 0;
+                               else
+                                       // we assume true is greater than false
+                                       rc = b1 ? 1 : -1;
+                               break;
+                       case PropertyType.DATE:
+                               Calendar c1 = v1.getDate();
+                               Calendar c2 = v2.getDate();
+                               if (c1 == null || c2 == null)
+                                       // log.trace("undefined date");
+                                       ;
+                               lc = c1.getTimeInMillis() - c2.getTimeInMillis();
+                               if (lc < Integer.MIN_VALUE)
+                                       // rc = Integer.MIN_VALUE;
+                                       rc = -1;
+                               else if (lc > Integer.MAX_VALUE)
+                                       // rc = Integer.MAX_VALUE;
+                                       rc = 1;
+                               else
+                                       rc = (int) lc;
+                               break;
+                       case PropertyType.LONG:
+                               long l1;
+                               long l2;
+                               // FIXME sometimes an empty string is set instead of a long
+                               try {
+                                       l1 = v1.getLong();
+                               } catch (ValueFormatException ve) {
+                                       l1 = 0;
+                               }
+                               try {
+                                       l2 = v2.getLong();
+                               } catch (ValueFormatException ve) {
+                                       l2 = 0;
+                               }
+
+                               lc = l1 - l2;
+                               if (lc < Integer.MIN_VALUE)
+                                       rc = -1;
+                               else if (lc > Integer.MAX_VALUE)
+                                       rc = 1;
+                               else
+                                       rc = (int) lc;
+                               break;
+                       case PropertyType.DECIMAL:
+                               BigDecimal bd1 = v1.getDecimal();
+                               BigDecimal bd2 = v2.getDecimal();
+                               rc = bd1.compareTo(bd2);
+                               break;
+                       case PropertyType.DOUBLE:
+                               Double d1 = v1.getDouble();
+                               Double d2 = v2.getDouble();
+                               rc = d1.compareTo(d2);
+                               break;
+                       default:
+                               throw new ArgeoException(
+                                               "Unimplemented comparaison for PropertyType "
+                                                               + propertyType);
+                       }
+                       // If descending order, flip the direction
+                       if (direction == DESCENDING) {
+                               rc = -rc;
+                       }
+
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Unexpected error "
+                                       + "while comparing nodes", re);
+               }
+               return rc;
+       }
+
+       /**
+        * @param propertyType
+        *            Corresponding JCR type
+        * @param propertyName
+        *            name of the property to use.
+        */
+       public void setColumn(int propertyType, String propertyName) {
+               if (this.propertyName != null && this.propertyName.equals(propertyName)) {
+                       // Same column as last sort; toggle the direction
+                       direction = 1 - direction;
+               } else {
+                       // New column; do an ascending sort
+                       this.propertyType = propertyType;
+                       this.propertyName = propertyName;
+                       direction = ASCENDING;
+               }
+       }
+
+       // Getters and setters
+       protected String getPropertyName() {
+               return propertyName;
+       }
+
+       protected void setPropertyName(String propertyName) {
+               this.propertyName = propertyName;
+       }
+
+       protected int getPropertyType() {
+               return propertyType;
+       }
+
+       protected void setPropertyType(int propertyType) {
+               this.propertyType = propertyType;
+       }
+
+       protected int getDirection() {
+               return direction;
+       }
+
+       protected void setDirection(int direction) {
+               this.direction = direction;
+       }
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java
new file mode 100644 (file)
index 0000000..509f723
--- /dev/null
@@ -0,0 +1,62 @@
+package org.argeo.eclipse.ui.jcr.lists;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.Row;
+
+import org.argeo.ArgeoException;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Base comparator to enable ordering on Table or Tree viewer that display Jcr
+ * rows
+ */
+public class RowViewerComparator extends NodeViewerComparator {
+
+       protected String selectorName;
+
+       public RowViewerComparator() {
+       }
+
+       /**
+        * e1 and e2 must both be Jcr rows.
+        * 
+        * @param viewer
+        * @param e1
+        * @param e2
+        * @return
+        */
+       @Override
+       public int compare(Viewer viewer, Object e1, Object e2) {
+               try {
+                       Node n1 = ((Row) e1).getNode(selectorName);
+                       Node n2 = ((Row) e2).getNode(selectorName);
+                       return super.compare(viewer, n1, n2);
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Unexpected error "
+                                       + "while comparing nodes", re);
+               }
+       }
+
+       /**
+        * @param propertyType
+        *            Corresponding JCR type
+        * @param propertyName
+        *            name of the property to use.
+        */
+       public void setColumn(int propertyType, String selectorName,
+                       String propertyName) {
+               if (this.selectorName != null && getPropertyName() != null
+                               && this.selectorName.equals(selectorName)
+                               && this.getPropertyName().equals(propertyName)) {
+                       // Same column as last sort; toggle the direction
+                       setDirection(1 - getDirection());
+               } else {
+                       // New column; do a descending sort
+                       setPropertyType(propertyType);
+                       setPropertyName(propertyName);
+                       this.selectorName = selectorName;
+                       setDirection(NodeViewerComparator.ASCENDING);
+               }
+       }
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java
new file mode 100644 (file)
index 0000000..88585c3
--- /dev/null
@@ -0,0 +1,122 @@
+package org.argeo.eclipse.ui.jcr.lists;
+
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.text.SimpleDateFormat;
+
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import org.argeo.ArgeoException;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+
+/**
+ * Base implementation of a label provider for widgets that display JCR Rows.
+ */
+public class SimpleJcrNodeLabelProvider extends ColumnLabelProvider {
+       private static final long serialVersionUID = -5215787695436221993L;
+
+       private final static String DEFAULT_DATE_FORMAT = "EEE, dd MMM yyyy";
+       private final static String DEFAULT_NUMBER_FORMAT = "#,##0.0";
+
+       private DateFormat dateFormat;
+       private NumberFormat numberFormat;
+
+       final private String propertyName;
+
+       /**
+        * Default Label provider for a given property of a node. Using default
+        * pattern for date and number formating
+        */
+       public SimpleJcrNodeLabelProvider(String propertyName) {
+               this.propertyName = propertyName;
+               dateFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT);
+               numberFormat = DecimalFormat.getInstance();
+               ((DecimalFormat) numberFormat).applyPattern(DEFAULT_NUMBER_FORMAT);
+       }
+
+       /**
+        * Label provider for a given property of a node optionally precising date
+        * and/or number format patterns
+        */
+       public SimpleJcrNodeLabelProvider(String propertyName,
+                       String dateFormatPattern, String numberFormatPattern) {
+               this.propertyName = propertyName;
+               dateFormat = new SimpleDateFormat(
+                               dateFormatPattern == null ? DEFAULT_DATE_FORMAT
+                                               : dateFormatPattern);
+               numberFormat = DecimalFormat.getInstance();
+               ((DecimalFormat) numberFormat)
+                               .applyPattern(numberFormatPattern == null ? DEFAULT_NUMBER_FORMAT
+                                               : numberFormatPattern);
+       }
+
+       @Override
+       public String getText(Object element) {
+               try {
+                       Node currNode = (Node) element;
+
+                       if (currNode.hasProperty(propertyName)) {
+                               if (currNode.getProperty(propertyName).isMultiple()) {
+                                       StringBuilder builder = new StringBuilder();
+                                       for (Value value : currNode.getProperty(propertyName)
+                                                       .getValues()) {
+                                               String currStr = getSingleValueAsString(value);
+                                               if (notEmptyString(currStr))
+                                                       builder.append(currStr).append("; ");
+                                       }
+                                       if (builder.length() > 0)
+                                               builder.deleteCharAt(builder.length() - 2);
+
+                                       return builder.toString();
+                               } else
+                                       return getSingleValueAsString(currNode.getProperty(
+                                                       propertyName).getValue());
+                       } else
+                               return "";
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Unable to get text from row", re);
+               }
+       }
+
+       private String getSingleValueAsString(Value value)
+                       throws RepositoryException {
+               switch (value.getType()) {
+               case PropertyType.STRING:
+                       return value.getString();
+               case PropertyType.BOOLEAN:
+                       return "" + value.getBoolean();
+               case PropertyType.DATE:
+                       return dateFormat.format(value.getDate().getTime());
+               case PropertyType.LONG:
+                       return "" + value.getLong();
+               case PropertyType.DECIMAL:
+                       return numberFormat.format(value.getDecimal());
+               case PropertyType.DOUBLE:
+                       return numberFormat.format(value.getDouble());
+               case PropertyType.NAME:
+                       return value.getString();
+               default:
+                       throw new ArgeoException("Unimplemented label provider "
+                                       + "for property type " + value.getType()
+                                       + " while getting property " + propertyName + " - value: "
+                                       + value.getString());
+
+               }
+       }
+
+       private boolean notEmptyString(String string) {
+               return string != null && !"".equals(string.trim());
+       }
+
+       public void setDateFormat(String dateFormatPattern) {
+               dateFormat = new SimpleDateFormat(dateFormatPattern);
+       }
+
+       public void setNumberFormat(String numberFormatPattern) {
+               ((DecimalFormat) numberFormat).applyPattern(numberFormatPattern);
+       }
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java
new file mode 100644 (file)
index 0000000..bb55f18
--- /dev/null
@@ -0,0 +1,47 @@
+package org.argeo.eclipse.ui.jcr.lists;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.Row;
+
+import org.argeo.ArgeoException;
+
+/**
+ * Base implementation of a label provider for widgets that display JCR Rows.
+ */
+public class SimpleJcrRowLabelProvider extends SimpleJcrNodeLabelProvider {
+       private static final long serialVersionUID = -3414654948197181740L;
+
+       final private String selectorName;
+
+       /**
+        * Default Label provider for a given property of a row. Using default
+        * pattern for date and number formating
+        */
+       public SimpleJcrRowLabelProvider(String selectorName, String propertyName) {
+               super(propertyName);
+               this.selectorName = selectorName;
+       }
+
+       /**
+        * Label provider for a given property of a node optionally precising date
+        * and/or number format patterns
+        */
+       public SimpleJcrRowLabelProvider(String selectorName, String propertyName,
+                       String dateFormatPattern, String numberFormatPattern) {
+               super(propertyName, dateFormatPattern, numberFormatPattern);
+               this.selectorName = selectorName;
+       }
+
+       @Override
+       public String getText(Object element) {
+               try {
+                       Row currRow = (Row) element;
+                       Node currNode = currRow.getNode(selectorName);
+                       return super.getText(currNode);
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Unable to get Node " + selectorName
+                                       + " from row " + element, re);
+               }
+       }
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/utils/JcrFileProvider.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/utils/JcrFileProvider.java
new file mode 100644 (file)
index 0000000..47cab6d
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * 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.eclipse.ui.jcr.utils;
+
+import java.io.InputStream;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.specific.FileProvider;
+
+/**
+ * Implements a FileProvider for UI purposes. Note that it might not be very
+ * reliable as long as we have not fixed login & multi repository issues that
+ * will be addressed in the next version.
+ * 
+ * NOTE: id used here is the real id of the JCR Node, not the JCR Path
+ * 
+ * Relies on common approach for JCR file handling implementation.
+ * 
+ */
+
+public class JcrFileProvider implements FileProvider {
+
+       // private Object[] rootNodes;
+       private Node refNode;
+
+       /**
+        * Must be set in order for the provider to be able to get current session
+        * and thus have the ability to get the file node corresponding to a given
+        * file ID
+        * 
+        * FIXME : this introduces some concurrences ISSUES.
+        * 
+        * @param repositoryNode
+        */
+       public void setReferenceNode(Node refNode) {
+               this.refNode = refNode;
+       }
+
+       /**
+        * Must be set in order for the provider to be able to search the repository
+        * Provided object might be either JCR Nodes or UI RepositoryNode for the
+        * time being.
+        * 
+        * @param repositoryNode
+        */
+       // public void setRootNodes(Object[] rootNodes) {
+       // List<Object> tmpNodes = new ArrayList<Object>();
+       // for (int i = 0; i < rootNodes.length; i++) {
+       // Object obj = rootNodes[i];
+       // if (obj instanceof Node) {
+       // tmpNodes.add(obj);
+       // } else if (obj instanceof RepositoryRegister) {
+       // RepositoryRegister repositoryRegister = (RepositoryRegister) obj;
+       // Map<String, Repository> repositories = repositoryRegister
+       // .getRepositories();
+       // for (String name : repositories.keySet()) {
+       // // tmpNodes.add(new RepositoryNode(name, repositories
+       // // .get(name)));
+       // }
+       //
+       // }
+       // }
+       // this.rootNodes = tmpNodes.toArray();
+       // }
+
+       public byte[] getByteArrayFileFromId(String fileId) {
+               InputStream fis = null;
+               byte[] ba = null;
+               Node child = getFileNodeFromId(fileId);
+               try {
+                       fis = (InputStream) child.getProperty(Property.JCR_DATA)
+                                       .getBinary().getStream();
+                       ba = IOUtils.toByteArray(fis);
+
+               } catch (Exception e) {
+                       throw new ArgeoException("Stream error while opening file", e);
+               } finally {
+                       IOUtils.closeQuietly(fis);
+               }
+               return ba;
+       }
+
+       public InputStream getInputStreamFromFileId(String fileId) {
+               try {
+                       InputStream fis = null;
+
+                       Node child = getFileNodeFromId(fileId);
+                       fis = (InputStream) child.getProperty(Property.JCR_DATA)
+                                       .getBinary().getStream();
+                       return fis;
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Cannot get stream from file node for Id "
+                                       + fileId, re);
+               }
+       }
+
+       /**
+        * Throws an exception if the node is not found in the current repository (a
+        * bit like a FileNotFoundException)
+        * 
+        * @param fileId
+        * @return Returns the child node of the nt:file node. It is the child node
+        *         that have the jcr:data property where actual file is stored.
+        *         never null
+        */
+       private Node getFileNodeFromId(String fileId) {
+               try {
+                       Node result = refNode.getSession().getNodeByIdentifier(fileId);
+
+                       // rootNodes: for (int j = 0; j < rootNodes.length; j++) {
+                       // // in case we have a classic JCR Node
+                       // if (rootNodes[j] instanceof Node) {
+                       // Node curNode = (Node) rootNodes[j];
+                       // if (result != null)
+                       // break rootNodes;
+                       // } // Case of a repository Node
+                       // else if (rootNodes[j] instanceof RepositoryNode) {
+                       // Object[] nodes = ((RepositoryNode) rootNodes[j])
+                       // .getChildren();
+                       // for (int i = 0; i < nodes.length; i++) {
+                       // Node node = (Node) nodes[i];
+                       // result = node.getSession().getNodeByIdentifier(fileId);
+                       // if (result != null)
+                       // break rootNodes;
+                       // }
+                       // }
+                       // }
+
+                       // Sanity checks
+                       if (result == null)
+                               throw new ArgeoException("File node not found for ID" + fileId);
+
+                       Node child = null;
+                       
+                       boolean isValid = true;
+                       if (!result.isNodeType(NodeType.NT_FILE)) 
+                               // useless: mandatory child node
+                               // || !result.hasNode(Property.JCR_CONTENT)) 
+                               isValid = false;
+                       else {
+                               child = result.getNode(Property.JCR_CONTENT);
+                               if (!(child.isNodeType(NodeType.NT_RESOURCE) || child
+                                                       .hasProperty(Property.JCR_DATA)))
+                                       isValid = false;
+                       }
+
+                       if (!isValid)
+                               throw new ArgeoException(
+                                               "ERROR: In the current implemented model, '"
+                                                               + NodeType.NT_FILE
+                                                               + "' file node must have a child node named jcr:content "
+                                                               + "that has a BINARY Property named jcr:data "
+                                                               + "where the actual data is stored");
+                       return child;
+
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Erreur while getting file node of ID "
+                                       + fileId, re);
+               }
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/utils/JcrItemsComparator.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/utils/JcrItemsComparator.java
new file mode 100644 (file)
index 0000000..2d36bf2
--- /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.eclipse.ui.jcr.utils;
+
+import java.util.Comparator;
+
+import javax.jcr.Item;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+
+/** Compares two JCR items (node or properties) based on their names. */
+public class JcrItemsComparator implements Comparator<Item> {
+       public int compare(Item o1, Item o2) {
+               try {
+                       // TODO: put folder before files
+                       return o1.getName().compareTo(o2.getName());
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot compare " + o1 + " and " + o2, e);
+               }
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/utils/NodeViewerComparer.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/utils/NodeViewerComparer.java
new file mode 100644 (file)
index 0000000..5a94ee7
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.eclipse.ui.jcr.utils;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+import org.eclipse.jface.viewers.IElementComparer;
+
+/** Compare JCR nodes based on their JCR identifiers, for use in JFace viewers. */
+public class NodeViewerComparer implements IElementComparer {
+
+       // force comparison on Node IDs only.
+       public boolean equals(Object elementA, Object elementB) {
+               if (!(elementA instanceof Node) || !(elementB instanceof Node)) {
+                       return elementA == null ? elementB == null : elementA
+                                       .equals(elementB);
+               } else {
+
+                       boolean result = false;
+                       try {
+                               String idA = ((Node) elementA).getIdentifier();
+                               String idB = ((Node) elementB).getIdentifier();
+                               result = idA == null ? idB == null : idA.equals(idB);
+                       } catch (RepositoryException re) {
+                               throw new ArgeoException("cannot compare nodes", re);
+                       }
+
+                       return result;
+               }
+       }
+
+       public int hashCode(Object element) {
+               // TODO enhanced this method.
+               return element.getClass().toString().hashCode();
+       }
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/utils/SingleSessionFileProvider.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/utils/SingleSessionFileProvider.java
new file mode 100644 (file)
index 0000000..f0c3780
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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.eclipse.ui.jcr.utils;
+
+import java.io.InputStream;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.specific.FileProvider;
+
+/**
+ * Implements a FileProvider for UI purposes. Unlike the
+ * <code> JcrFileProvider </code>, it relies on a single session and manages
+ * nodes with path only.
+ * 
+ * Note that considered id is the JCR path
+ * 
+ * Relies on common approach for JCR file handling implementation.
+ * 
+ * @author bsinou
+ * 
+ */
+
+public class SingleSessionFileProvider implements FileProvider {
+
+       private Session session;
+
+       public SingleSessionFileProvider(Session session) {
+               this.session = session;
+       }
+
+       public byte[] getByteArrayFileFromId(String fileId) {
+               InputStream fis = null;
+               byte[] ba = null;
+               Node child = getFileNodeFromId(fileId);
+               try {
+                       fis = (InputStream) child.getProperty(Property.JCR_DATA)
+                                       .getBinary().getStream();
+                       ba = IOUtils.toByteArray(fis);
+
+               } catch (Exception e) {
+                       throw new ArgeoException("Stream error while opening file", e);
+               } finally {
+                       IOUtils.closeQuietly(fis);
+               }
+               return ba;
+       }
+
+       public InputStream getInputStreamFromFileId(String fileId) {
+               try {
+                       InputStream fis = null;
+
+                       Node child = getFileNodeFromId(fileId);
+                       fis = (InputStream) child.getProperty(Property.JCR_DATA)
+                                       .getBinary().getStream();
+                       return fis;
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Cannot get stream from file node for Id "
+                                       + fileId, re);
+               }
+       }
+
+       /**
+        * 
+        * @param fileId
+        * @return Returns the child node of the nt:file node. It is the child node
+        *         that have the jcr:data property where actual file is stored.
+        *         never null
+        */
+       private Node getFileNodeFromId(String fileId) {
+               try {
+                       Node result = null;
+                       result = session.getNode(fileId);
+
+                       // Sanity checks
+                       if (result == null)
+                               throw new ArgeoException("File node not found for ID" + fileId);
+
+                       // Ensure that the node have the correct type.
+                       if (!result.isNodeType(NodeType.NT_FILE))
+                               throw new ArgeoException(
+                                               "Cannot open file children Node that are not of "
+                                                               + NodeType.NT_RESOURCE + " type.");
+
+                       Node child = result.getNodes().nextNode();
+                       if (child == null || !child.isNodeType(NodeType.NT_RESOURCE))
+                               throw new ArgeoException(
+                                               "ERROR: IN the current implemented model, "
+                                                               + NodeType.NT_FILE
+                                                               + "  file node must have one and only one child of the nt:ressource, where actual data is stored");
+                       return child;
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Erreur while getting file node of ID "
+                                       + fileId, re);
+               }
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/views/AbstractJcrBrowser.java b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/views/AbstractJcrBrowser.java
new file mode 100644 (file)
index 0000000..133a65f
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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.eclipse.ui.jcr.views;
+
+import javax.jcr.Node;
+
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.part.ViewPart;
+
+public abstract class AbstractJcrBrowser extends ViewPart {
+
+       @Override
+       public abstract void createPartControl(Composite parent);
+
+       /**
+        * To be overridden to adapt size of form and result frames.
+        */
+       abstract protected int[] getWeights();
+
+       /**
+        * To be overridden to provide an adapted size nodeViewer
+        */
+       abstract protected TreeViewer createNodeViewer(Composite parent,
+                       ITreeContentProvider nodeContentProvider);
+
+       /**
+        * To be overridden to retrieve the current nodeViewer
+        */
+       abstract protected TreeViewer getNodeViewer();
+
+       /*
+        * Enables the refresh of the tree.
+        */
+       @Override
+       public void setFocus() {
+               getNodeViewer().getTree().setFocus();
+       }
+
+       public void refresh(Object obj) {
+               // getNodeViewer().update(obj, null);
+               getNodeViewer().refresh(obj);
+               // getNodeViewer().expandToLevel(obj, 1);
+       }
+
+       public void nodeAdded(Node parentNode, Node newNode) {
+               getNodeViewer().refresh(parentNode);
+               getNodeViewer().expandToLevel(newNode, 0);
+       }
+
+       public void nodeRemoved(Node parentNode) {
+               IStructuredSelection newSel = new StructuredSelection(parentNode);
+               getNodeViewer().setSelection(newSel, true);
+               // Force refresh
+               IStructuredSelection tmpSel = (IStructuredSelection) getNodeViewer()
+                               .getSelection();
+               getNodeViewer().refresh(tmpSel.getFirstElement());
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/resources/org/argeo/eclipse/ui/jcr/messages.properties b/trunk/base/runtime/org.argeo.eclipse.ui.jcr/src/main/resources/org/argeo/eclipse/ui/jcr/messages.properties
new file mode 100644 (file)
index 0000000..a24aa38
--- /dev/null
@@ -0,0 +1,7 @@
+## English labels for Agreo base JCR UI application 
+
+## Generic labels 
+
+## Errors
+errorUnvalidNtFolderNodeType= Error: folder can only be created on a Jcr Node
+
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/.classpath b/trunk/base/runtime/org.argeo.eclipse.ui.rap/.classpath
new file mode 100644 (file)
index 0000000..8cf7f48
--- /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.6"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/.project b/trunk/base/runtime/org.argeo.eclipse.ui.rap/.project
new file mode 100644 (file)
index 0000000..df496c2
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.eclipse.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/trunk/base/runtime/org.argeo.eclipse.ui.rap/.settings/org.eclipse.jdt.core.prefs b/trunk/base/runtime/org.argeo.eclipse.ui.rap/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..ffba01e
--- /dev/null
@@ -0,0 +1,8 @@
+#Wed Mar 02 13:27:08 CET 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/build.properties b/trunk/base/runtime/org.argeo.eclipse.ui.rap/build.properties
new file mode 100644 (file)
index 0000000..7cc98e7
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .,\
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/pom.xml b/trunk/base/runtime/org.argeo.eclipse.ui.rap/pom.xml
new file mode 100644 (file)
index 0000000..b27cea2
--- /dev/null
@@ -0,0 +1,78 @@
+<?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.base</groupId>
+               <version>2.1.11</version>
+               <artifactId>runtime</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.eclipse.ui.rap</artifactId>
+       <name>Commons Eclipse UI RAP</name>
+       <packaging>jar</packaging>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-ActivationPolicy>lazy</Bundle-ActivationPolicy>
+                                               <Bundle-Activator>org.argeo.eclipse.ui.ArgeoUiPlugin</Bundle-Activator>
+                                               <Require-Bundle>org.eclipse.rap.ui,org.eclipse.core.runtime</Require-Bundle>
+                                               <!-- IMPORTANT : Note that we must exclude certain packages that are 
+                                                       provided by dependencies to be sure that packages used in the current workspace 
+                                                       are those provided by the require-bundles. Especially rwt.widgets.upload 
+                                                       by instance. -->
+                                               <Import-Package>
+                                                       org.apache.commons.io,
+                                                       org.argeo,
+                                                       org.springframework.beans.factory,
+                                                       org.springframework.core.io.support,
+                                                       !org.eclipse.rwt.widgets,
+                                                       !org.eclipse.core.runtime,
+                                                       !org.eclipse.core.commands,
+                                                       !org.eclipse.ui.plugin,
+                                                       *
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+
+       <!-- deleted from import package : javax.servlet, javax.servlet.http,org.argeo.eclipse.ui.dialogs, 
+               org.eclipse.jface.dialogs, -->
+       <dependencies>
+                       <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+       
+               <!-- Argeo common distribution for RAP projects -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.dep.rap</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/DownloadServiceHandler.java b/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/DownloadServiceHandler.java
new file mode 100644 (file)
index 0000000..b282296
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.argeo.ArgeoException;
+import org.eclipse.rap.rwt.service.ServiceHandler;
+
+@Deprecated
+public class DownloadServiceHandler implements ServiceHandler {
+
+       private FileProvider provider;
+
+       public DownloadServiceHandler(FileProvider provider) {
+               this.provider = provider;
+       }
+
+       public void service(HttpServletRequest request, HttpServletResponse response)
+                       throws IOException, ServletException {
+               // Which file to download?
+               String fileName = request.getParameter("filename");
+               String fileId = request.getParameter("fileid");
+
+               // Get the file content
+               byte[] download = provider.getByteArrayFileFromId(fileId);
+
+               // Send the file in the response
+               response.setContentType("application/octet-stream");
+               response.setContentLength(download.length);
+               String contentDisposition = "attachment; filename=\"" + fileName + "\"";
+               response.setHeader("Content-Disposition", contentDisposition);
+
+               // Various header fields that can be set to solve some issues with some
+               // old browsers.
+               // Unused.
+               // String contentType = "application/force-download; name=\"" + fileName
+               // + "\"";
+               // response.setContentType(contentType);
+               // response.setHeader("Content-Transfer-Encoding", "binary");
+               // response.setHeader("Pragma", "no-cache");
+               // response.setHeader("Cache-Control", "no-cache, must-revalidate");
+               // response.setHeader("Expires", "0");
+               // response.setHeader("Connection", "Keep-Alive");
+               // response.setHeader("Keep-Alive", "timeout=5, max=86");
+               // response.setHeader("transfer-Encoding", "chunked");
+
+               try {
+                       response.getOutputStream().write(download);
+               } catch (IOException ioe) {
+                       throw new ArgeoException("Error while writing the file " + fileName
+                                       + " to the servlet response", ioe);
+               }
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java b/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java
new file mode 100644 (file)
index 0000000..c7ab28a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import org.eclipse.jface.viewers.AbstractTableViewer;
+import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
+import org.eclipse.jface.viewers.Viewer;
+
+/** Static utilities to bridge differences between RCP and RAP */
+public class EclipseUiSpecificUtils {
+       /**
+        * TootlTip support is supported only for {@link AbstractTableViewer} in RAP
+        * 
+        * @see ColumnViewerToolTipSupport#enableFor(AbstractTableViewer)
+        */
+       public static void enableToolTipSupport(Viewer viewer) {
+               if (viewer instanceof AbstractTableViewer)
+                       ColumnViewerToolTipSupport.enableFor((AbstractTableViewer) viewer);
+       }
+
+       private EclipseUiSpecificUtils() {
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/FileHandler.java b/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/FileHandler.java
new file mode 100644 (file)
index 0000000..927747e
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.client.service.UrlLauncher;
+import org.eclipse.rap.rwt.service.ServiceHandler;
+
+/**
+ * RAP SPECIFIC handler to enable the opening of a download dialog box triggered
+ * by whatever action in the UI
+ * 
+ * Manages the registration of the effective DownloadServiceHandler at
+ * instantiation time.
+ * 
+ * Manages the process of forwarding the request to the handler at runtime to
+ * open the dialog box encodedURL
+ */
+@Deprecated
+public class FileHandler {
+       public final static String DOWNLOAD_SERVICE_NAME = "argeo.rap.download.service";
+       private final static Log log = LogFactory.getLog(FileHandler.class);
+
+       public FileHandler(FileProvider provider) {
+               ServiceHandler handler = new DownloadServiceHandler(provider);
+               try {
+                       RWT.getServiceManager().registerServiceHandler(
+                                       DOWNLOAD_SERVICE_NAME, handler);
+               } catch (IllegalArgumentException iae) {
+                       log.warn("Handler is already registered, clean this registering process");
+               }
+       }
+
+       public void openFile(String fileName, String fileId) {
+               try {
+                       String downloadUrl = RWT.getServiceManager().getServiceHandlerUrl(
+                                       DOWNLOAD_SERVICE_NAME)
+                                       + createParamUrl(fileName, fileId);
+                       if (log.isTraceEnabled())
+                               log.debug("URL : " + downloadUrl);
+                       UrlLauncher launcher = RWT.getClient()
+                                       .getService(UrlLauncher.class);
+                       launcher.openURL(downloadUrl);
+               } catch (Exception e) {
+                       throw new ArgeoException("Unable to open file " + fileName, e);
+               }
+               // These lines are useless in the current use case but might be
+               // necessary with new browsers. Stored here for memo
+               // response.setContentType("application/force-download");
+               // response.setHeader("Content-Disposition", contentDisposition);
+               // response.setHeader("Content-Transfer-Encoding", "binary");
+               // response.setHeader("Pragma", "no-cache");
+               // response.setHeader("Cache-Control", "no-cache, must-revalidate");
+       }
+
+       private String createParamUrl(String filename, String fileId) {
+               StringBuilder url = new StringBuilder();
+               url.append("&filename=");
+               url.append(filename);
+               url.append("&fileid=");
+               url.append(fileId);
+               return url.toString();
+       }
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/FileProvider.java b/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/FileProvider.java
new file mode 100644 (file)
index 0000000..ae959fb
--- /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.eclipse.ui.specific;
+
+import java.io.InputStream;
+
+/**
+ * Used for file download : subclasses must implement model specific methods to
+ * get a byte array representing a file given is ID.
+ */
+@Deprecated
+public interface FileProvider {
+
+       public byte[] getByteArrayFileFromId(String fileId);
+
+       public InputStream getInputStreamFromFileId(String fileId);
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/GenericUploadControl.java b/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/GenericUploadControl.java
new file mode 100644 (file)
index 0000000..e3b3198
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import java.io.InputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+//import org.eclipse.rap.rwt.widgets.Upload;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+public class GenericUploadControl extends Composite {
+       private final static Log log = LogFactory
+                       .getLog(GenericUploadControl.class);
+
+       //private Upload upload;
+
+       public GenericUploadControl(Composite parent, int style, String browseLabel) {
+               super(parent, style);
+               createControl(this, browseLabel);
+
+       }
+
+       private void createControl(Composite parent, String browseLabel) {
+               parent.setLayout(new GridLayout(1, false));
+               parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+//             upload = new Upload(parent, SWT.BORDER);
+//             upload.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+//             upload.setBrowseButtonText(browseLabel);
+               // upload.addModifyListener(new UploadListener());
+               parent.pack();
+       }
+
+       /**
+        * Wrap upload.getLastFileUploaded(). Gets the name of the last uploaded
+        * file. This method can be called even if the upload has not finished yet.
+        */
+       public String getLastFileUploadedName() {
+               return "";
+       }
+
+       public boolean isControlEmpty() {
+               String path = "";
+               if (log.isTraceEnabled())
+                       log.trace("UploadControl chosen path : " + path);
+               if (path == null || "".equals(path.trim()))
+                       return true;
+               else
+                       return false;
+       }
+
+       public byte[] performUpload() {
+//             boolean success = upload.performUpload();
+//             if (success) {
+//                     if (upload.getUploadItem().getFileSize() == -1)
+//                             throw new ArgeoException("File "
+//                                             + upload.getUploadItem().getFileName()
+//                                             + " has not been uploaded, its size is -1");
+//
+//                     InputStream inStream = null;
+//                     byte[] fileBA = null;
+//                     try {
+//                             inStream = upload.getUploadItem().getFileInputStream();
+//                             fileBA = IOUtils.toByteArray(inStream);
+//                     } catch (Exception e) {
+//                             throw new ArgeoException("Cannot read uploaded data", e);
+//                     } finally {
+//                             IOUtils.closeQuietly(inStream);
+//                     }
+//                     return fileBA;
+//             }
+               return null;
+       }
+
+       public void addModifyListener(ModifyListener listener) {
+//             upload.addModifyListener(listener);
+       }
+
+       // private class UploadManager extends UploadAdapter {
+       // private Upload upload;
+       //
+       // public UploadManager(Upload upload) {
+       // super();
+       // this.upload = upload;
+       // }
+       //
+       // public void uploadFinished(UploadEvent uploadEvent) {
+       // handleUploadFinished(upload);
+       // }
+       //
+       // public void uploadInProgress(UploadEvent uploadEvent) {
+       // }
+       //
+       // public void uploadException(UploadEvent uploadEvent) {
+       // Exception exc = uploadEvent.getUploadException();
+       // if (exc != null) {
+       // MessageDialog.openError(Display.getCurrent().getActiveShell(),
+       // "Error", exc.getMessage());
+       // }
+       // }
+       //
+       // }
+       //
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/ImportToServerWizardPage.java b/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/ImportToServerWizardPage.java
new file mode 100644 (file)
index 0000000..f66a279
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import java.io.InputStream;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.eclipse.jface.wizard.WizardPage;
+//import org.eclipse.rap.rwt.widgets.Upload;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+public class ImportToServerWizardPage extends WizardPage {
+       private final static Log log = LogFactory
+                       .getLog(ImportToServerWizardPage.class);
+
+       public final static String FILE_ITEM_TYPE = "FILE";
+       public final static String FOLDER_ITEM_TYPE = "FOLDER";
+
+       //private Upload uploadFile;
+
+       public ImportToServerWizardPage() {
+               super("Import from file system");
+               setDescription("Import files from the local file system to the server");
+       }
+
+       public void createControl(Composite parent) {
+               Composite composite = new Composite(parent, SWT.NONE);
+               composite.setLayout(new GridLayout(2, false));
+               composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               new Label(composite, SWT.NONE).setText("Pick up a file");
+//             uploadFile = new Upload(composite, SWT.BORDER);
+//             uploadFile.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+//             uploadFile.setBrowseButtonText("Open...");
+               setControl(composite);
+       }
+
+       public String getObjectPath() {
+               // NOTE Returns the full file name of the last uploaded file including
+               // the file path as selected by the user on his local machine.
+               // The full path including the directory and file drive are only
+               // returned, if the browser supports reading this properties. In Firefox
+               // 3, only the filename is returned.
+               return null;
+       }
+
+       public String getObjectName() {
+               return null;
+       }
+
+       public String getObjectType() {
+               return FILE_ITEM_TYPE;
+       }
+
+       public void performFinish() {
+//             boolean success = uploadFile.performUpload();
+//             if (!success)
+//                     throw new ArgeoException("Cannot upload file named "
+//                                     + uploadFile.getPath());
+       }
+
+//     protected void handleUploadFinished(final Upload upload) {
+//     }
+
+       public InputStream getFileInputStream() {
+               return null;
+       }
+
+       public boolean getNeedsProgressMonitor() {
+               return false;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/OpenFile.java b/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/OpenFile.java
new file mode 100644 (file)
index 0000000..b55521b
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.client.service.UrlLauncher;
+
+/**
+ * Rap specific command handler to open a file retrieved from the server. It
+ * forwards the request to the correct service after encoding file name and path
+ * in the request URI.
+ * 
+ * The parameter "URI" is used to determine the correct file service, the path
+ * and the file name. An optional file name can be precised to present a
+ * different file name as the one used to retrieve it to the end user/
+ * 
+ * Various instances of this handler with different command ID might coexist in
+ * order to provide context specific download service.
+ * 
+ * The instance specific service is called by its ID and must have been
+ * externally created
+ */
+public class OpenFile extends AbstractHandler {
+       private final static Log log = LogFactory.getLog(OpenFile.class);
+
+       /* DEPENDENCY INJECTION */
+       private String openFileServiceId;
+
+       public final static String PARAM_FILE_NAME = OpenFileService.PARAM_FILE_NAME;
+       public final static String PARAM_FILE_URI = OpenFileService.PARAM_FILE_URI; // "param.fileURI";
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               String fileName = event.getParameter(PARAM_FILE_NAME);
+               String fileUri = event.getParameter(PARAM_FILE_URI);
+
+               // sanity check
+               if (fileUri == null || "".equals(fileUri.trim())
+                               || openFileServiceId == null
+                               || "".equals(openFileServiceId.trim()))
+                       return null;
+
+               StringBuilder url = new StringBuilder();
+               url.append(RWT.getServiceManager().getServiceHandlerUrl(
+                               openFileServiceId));
+
+               url.append("&").append(PARAM_FILE_NAME).append("=");
+               url.append(fileName);
+               url.append("&").append(PARAM_FILE_URI).append("=");
+               url.append(fileUri);
+
+               String downloadUrl = url.toString();
+               if (log.isTraceEnabled())
+                       log.debug("URL : " + downloadUrl);
+
+               UrlLauncher launcher = RWT.getClient().getService(UrlLauncher.class);
+               launcher.openURL(downloadUrl);
+
+               // These lines are useless in the current use case but might be
+               // necessary with new browsers. Stored here for memo
+               // response.setContentType("application/force-download");
+               // response.setHeader("Content-Disposition", contentDisposition);
+               // response.setHeader("Content-Transfer-Encoding", "binary");
+               // response.setHeader("Pragma", "no-cache");
+               // response.setHeader("Cache-Control", "no-cache, must-revalidate");
+               return null;
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setOpenFileServiceId(String openFileServiceId) {
+               this.openFileServiceId = openFileServiceId;
+       }
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/OpenFileService.java b/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/OpenFileService.java
new file mode 100644 (file)
index 0000000..e48babf
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.FileUtils;
+import org.argeo.ArgeoException;
+import org.eclipse.rap.rwt.service.ServiceHandler;
+
+/**
+ * Basic Default service handler that retrieves a file on the server file system
+ * using its absolute path and forwards it to the end user browser. Rap
+ * specific.
+ * 
+ * Clients might extend to provide context specific services (to open files from
+ * a JCR repository for instance)
+ */
+public class OpenFileService implements ServiceHandler {
+       public final static String PARAM_FILE_NAME = "param.fileName";
+       public final static String PARAM_FILE_URI = "param.fileURI";
+
+       public final static String SCHEME_HOST_SEPARATOR = "://";
+       public final static String FILE_SCHEME = "file";
+
+       public OpenFileService() {
+       }
+
+       public void service(HttpServletRequest request, HttpServletResponse response)
+                       throws IOException, ServletException {
+               String fileName = request.getParameter(PARAM_FILE_NAME);
+               String uri = request.getParameter(PARAM_FILE_URI);
+
+               // Set the Metadata
+               response.setContentType("application/octet-stream");
+               response.setContentLength((int) getFileLength(uri));
+               if (fileName == null || "".equals(fileName.trim()))
+                       fileName = getFileName(uri);
+               String contentDisposition = "attachment; filename=\"" + fileName + "\"";
+               response.setHeader("Content-Disposition", contentDisposition);
+
+               response.getOutputStream().write(getFileAsByteArray(uri));
+               // FileUtils.readFileToByteArray(new File(path))
+       }
+
+       protected byte[] getFileAsByteArray(String uri) {
+               if (uri.startsWith(FILE_SCHEME)) {
+                       try {
+                               return FileUtils.readFileToByteArray(new File(
+                                               getFilePathFromUri(uri)));
+                       } catch (IOException ioe) {
+                               throw new ArgeoException("Error while getting the file at "
+                                               + uri, ioe);
+                       }
+               }
+               return null;
+       }
+
+       protected long getFileLength(String uri) {
+               if (uri.startsWith(FILE_SCHEME)) {
+                       return new File(getFilePathFromUri(uri)).length();
+               }
+               return -1l;
+       }
+
+       protected String getFileName(String uri) {
+               if (uri.startsWith(FILE_SCHEME)) {
+                       return new File(getFilePathFromUri(uri)).getName();
+               }
+               return null;
+       }
+
+       private String getFilePathFromUri(String uri) {
+               return uri.substring((FILE_SCHEME + SCHEME_HOST_SEPARATOR).length());
+       }
+
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/ThreadNLS.java b/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/ThreadNLS.java
new file mode 100644 (file)
index 0000000..8b8837f
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.rap.rwt.RWT;
+
+/** NLS attached to a given thread */
+public class ThreadNLS<T extends NLS> extends InheritableThreadLocal<T> {
+       public final static String DEFAULT_BUNDLE_LOCATION = "/properties/plugin";
+
+       private final String bundleLocation;
+
+       private Class<T> type;
+       private Boolean utf8 = false;
+
+       public ThreadNLS(String bundleLocation, Class<T> type, Boolean utf8) {
+               this.bundleLocation = bundleLocation;
+               this.type = type;
+               this.utf8 = utf8;
+       }
+
+       public ThreadNLS(Class<T> type) {
+               this(DEFAULT_BUNDLE_LOCATION, type, false);
+       }
+
+       @SuppressWarnings("unchecked")
+       @Override
+       protected T initialValue() {
+               if (utf8)
+                       return (T) RWT.NLS.getUTF8Encoded(bundleLocation, type);
+               else
+                       return (T) RWT.NLS.getISO8859_1Encoded(bundleLocation, type);
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/UploadFileWizardPage.java b/trunk/base/runtime/org.argeo.eclipse.ui.rap/src/main/java/org/argeo/eclipse/ui/specific/UploadFileWizardPage.java
new file mode 100644 (file)
index 0000000..17d4e22
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import java.io.InputStream;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.rap.rwt.widgets.FileUpload;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+public class UploadFileWizardPage extends WizardPage {
+       // private final static Log log = LogFactory
+       // .getLog(UploadFileWizardPage.class);
+       private static final long serialVersionUID = 8251354244542973179L;
+       public final static String FILE_ITEM_TYPE = "FILE";
+       public final static String FOLDER_ITEM_TYPE = "FOLDER";
+
+       private FileUpload fileUpload;
+
+       public UploadFileWizardPage() {
+               super("Import from file system");
+               setDescription("Import files from the local file system to the server");
+       }
+
+       public void createControl(Composite parent) {
+               Composite composite = new Composite(parent, SWT.NONE);
+               composite.setLayout(new GridLayout(2, false));
+               composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               new Label(composite, SWT.NONE).setText("Pick up a file");
+               fileUpload = new FileUpload(composite, SWT.BORDER);
+               fileUpload.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               fileUpload.setText("Browse");
+               setControl(composite);
+       }
+
+       public String getObjectPath() {
+               // NOTE Returns the full file name of the last uploaded file including
+               // the file path as selected by the user on his local machine.
+               // The full path including the directory and file drive are only
+               // returned, if the browser supports reading this properties. In Firefox
+               // 3, only the filename is returned.
+               return null;
+       }
+
+       public String getObjectName() {
+               return fileUpload.getFileName();
+       }
+
+       public String getObjectType() {
+               return FILE_ITEM_TYPE;
+       }
+
+       public void performFinish() {
+               // boolean success = uploadFile.performUpload();
+               // if (!success)
+               // throw new ArgeoException("Cannot upload file named "
+               // + uploadFile.getPath());
+       }
+
+       // protected void handleUploadFinished(final Upload upload) {
+       // }
+
+       public InputStream getFileInputStream() {
+               return null;
+       }
+
+       public boolean getNeedsProgressMonitor() {
+               return false;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rcp/.classpath b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/.classpath
new file mode 100644 (file)
index 0000000..8cf7f48
--- /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.6"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rcp/.project b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/.project
new file mode 100644 (file)
index 0000000..ef2dc2d
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.eclipse.ui.rcp</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/trunk/base/runtime/org.argeo.eclipse.ui.rcp/.settings/org.eclipse.jdt.core.prefs b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..b06d92e
--- /dev/null
@@ -0,0 +1,8 @@
+#Tue Mar 01 19:20:51 CET 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rcp/build.properties b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/build.properties
new file mode 100644 (file)
index 0000000..886a200
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .,\
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rcp/pom.xml b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/pom.xml
new file mode 100644 (file)
index 0000000..6f6b2f1
--- /dev/null
@@ -0,0 +1,84 @@
+<?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.base</groupId>
+               <version>2.1.11</version>
+               <artifactId>runtime</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.eclipse.ui.rcp</artifactId>
+       <name>Commons Eclipse UI RCP</name>
+       <packaging>jar</packaging>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-ActivationPolicy>lazy</Bundle-ActivationPolicy>
+                                               <Bundle-Activator>org.argeo.eclipse.ui.ArgeoUiPlugin</Bundle-Activator>
+                                               <Require-Bundle>org.eclipse.ui,org.eclipse.core.runtime</Require-Bundle>
+                                               <Import-Package>
+                                                       org.springframework.beans.factory,
+                                                       org.springframework.core.io.support,
+                                                       org.apache.commons.io,
+                                                       org.argeo,
+                                                       !org.eclipse.core.runtime,
+                                                       !org.eclipse.core.commands,
+                                                       !org.eclipse.ui.plugin,
+                                                       *
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- Argeo Commons for Eclipse (not specific) -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Argeo Commons third party distribution for RCP projects -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.dep.rcp</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Others. -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.io</artifactId>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/DefaultNLS.java b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/DefaultNLS.java
new file mode 100644 (file)
index 0000000..71a0c89
--- /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.eclipse.ui.specific;
+
+import org.eclipse.osgi.util.NLS;
+
+/** RCP specific {@link NLS} to be extended */
+public class DefaultNLS extends NLS {
+       public final static String DEFAULT_BUNDLE_LOCATION = "/properties/plugin";
+
+       public DefaultNLS() {
+               this(DEFAULT_BUNDLE_LOCATION);
+       }
+
+       public DefaultNLS(String bundleName) {
+               NLS.initializeMessages(bundleName, getClass());
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java
new file mode 100644 (file)
index 0000000..2642a35
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
+import org.eclipse.jface.viewers.Viewer;
+
+/** Static utilities to bridge differences between RCP and RAP */
+public class EclipseUiSpecificUtils {
+       /**
+        * TootlTip support is supported for {@link ColumnViewer} in RCP
+        * 
+        * @see ColumnViewerToolTipSupport#enableFor(Viewer)
+        */
+       public static void enableToolTipSupport(Viewer viewer) {
+               if (viewer instanceof ColumnViewer)
+                       ColumnViewerToolTipSupport.enableFor((ColumnViewer) viewer);
+       }
+
+       private EclipseUiSpecificUtils() {
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/FileHandler.java b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/FileHandler.java
new file mode 100644 (file)
index 0000000..5da4973
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import java.awt.Desktop;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.ArgeoException;
+
+/**
+ * Abstraction that enable to implement runtime environment specific (typically
+ * RCP or RAP) methods while dealing with files in the UI.
+ * 
+ */
+public class FileHandler {
+
+       private FileProvider provider;
+
+       public FileHandler(FileProvider provider) {
+               this.provider = provider;
+       }
+
+       public void openFile(String fileName, String fileId) {
+               String tmpFileName = fileName;
+               String prefix = "", extension = "";
+               if (fileName != null) {
+                       int ind = fileName.lastIndexOf('.');
+                       if (ind > 0) {
+                               prefix = fileName.substring(0, ind);
+                               extension = fileName.substring(ind);
+                       }
+               }
+
+               InputStream is = null;
+               try {
+                       is = provider.getInputStreamFromFileId(fileId);
+                       File file = createTmpFile(prefix, extension, is);
+                       tmpFileName = file.getName();
+                       Desktop desktop = null;
+                       if (Desktop.isDesktopSupported()) {
+                               desktop = Desktop.getDesktop();
+                       }
+                       desktop.open(file);
+               } catch (IOException e) {
+                       // Note : tmpFileName = fileName if the error has been thrown while
+                       // creating the tmpFile.
+                       throw new ArgeoException("Cannot open file " + tmpFileName, e);
+               } finally {
+                       IOUtils.closeQuietly(is);
+               }
+       }
+
+       private File createTmpFile(String prefix, String suffix, InputStream is) {
+               File tmpFile = null;
+               OutputStream os = null;
+               try {
+                       tmpFile = File.createTempFile(prefix, suffix);
+                       os = new FileOutputStream(tmpFile);
+                       IOUtils.copy(is, os);
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot open file " + prefix + "."
+                                       + suffix, e);
+               } finally {
+                       IOUtils.closeQuietly(os);
+               }
+               return tmpFile;
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/FileProvider.java b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/FileProvider.java
new file mode 100644 (file)
index 0000000..5c92260
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import java.io.InputStream;
+
+/**
+ * Used for file download : subclasses must implement model specific methods to
+ * get a byte array representing a file given is ID.
+ * 
+ * @author bsinou
+ * 
+ */
+public interface FileProvider {
+
+       public byte[] getByteArrayFileFromId(String fileId);
+       
+       public InputStream getInputStreamFromFileId(String fileId);
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/GenericUploadControl.java b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/GenericUploadControl.java
new file mode 100644 (file)
index 0000000..80e1e53
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.io.FileUtils;
+import org.argeo.ArgeoException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyListener;
+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.Event;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * RCP specific composite that provides a control to upload a file. WARNING: for
+ * the time being we set a GridLayout(2, false) on th eparent control.
+ */
+public class GenericUploadControl extends Composite {
+       // private final static Log log = LogFactory
+       // .getLog(GenericUploadControl.class);
+
+       private FileDialog dialog;
+       private Text filePath;
+
+       public GenericUploadControl(Composite parent, int style, String browseLabel) {
+               super(parent, style);
+               createControl(this, browseLabel);
+
+       }
+
+       private void createControl(final Composite parent, String browseLabel) {
+               parent.setLayout(new GridLayout(2, false));
+
+               filePath = new Text(parent, SWT.BORDER | SWT.SINGLE);
+               GridData gd = new GridData(GridData.GRAB_HORIZONTAL
+                               | GridData.FILL_HORIZONTAL);
+               filePath.setEditable(false);
+               filePath.setLayoutData(gd);
+
+               // Execute button
+               Button execute = new Button(parent, SWT.PUSH);
+               GridData gridData = new GridData();
+               gridData.horizontalAlignment = GridData.BEGINNING;
+               execute.setLayoutData(gridData);
+               execute.setText(browseLabel);
+
+               // Button listener
+               Listener executeListener = new Listener() {
+                       public void handleEvent(Event event) {
+                               dialog = new FileDialog(parent.getShell());
+                               filePath.setText(dialog.open());
+                       }
+               };
+               parent.layout();
+               execute.addListener(SWT.Selection, executeListener);
+       }
+
+       public boolean isControlEmpty() {
+               String path = filePath.getText();
+               if (path == null || "".equals(path.trim()))
+                       return true;
+               else
+                       return false;
+       }
+
+       public byte[] performUpload() {
+               String path = filePath.getText();
+               if (path != null) {
+                       try {
+                               File file = new File(path);
+                               byte[] fileBA = FileUtils.readFileToByteArray(file);
+                               return fileBA;
+                       } catch (IOException e) {
+                               throw new ArgeoException("Unexpected error while "
+                                               + "reading file at path " + path, e);
+                       }
+               }
+               return null;
+       }
+
+       public void addModifyListener(ModifyListener listener) {
+               filePath.addModifyListener(listener);
+       }
+
+       /**
+        * Always returns null in an RCP environment
+        */
+       public String getLastFileUploadedName() {
+               return null;
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/ImportToServerWizardPage.java b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/ImportToServerWizardPage.java
new file mode 100644 (file)
index 0000000..55065bf
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import java.io.InputStream;
+
+import org.eclipse.jface.preference.DirectoryFieldEditor;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.widgets.Composite;
+
+public class ImportToServerWizardPage extends WizardPage {
+
+       public final static String FILE_ITEM_TYPE = "FILE";
+       public final static String FOLDER_ITEM_TYPE = "FOLDER";
+
+       private DirectoryFieldEditor dfe;
+
+       public ImportToServerWizardPage() {
+               super("Import from file system");
+               setDescription("Import files from the local file system into the JCR repository");
+       }
+
+       public void createControl(Composite parent) {
+               dfe = new DirectoryFieldEditor("directory", "From", parent);
+               setControl(dfe.getTextControl(parent));
+       }
+
+       public String getObjectPath() {
+               return dfe.getStringValue();
+       }
+
+       public String getObjectType() {
+               return FOLDER_ITEM_TYPE;
+       }
+
+       public boolean getNeedsProgressMonitor() {
+               return true;
+       }
+
+       // Dummy methods : useless in RCP context but useful for RAP
+       /** WARNING : always return null in RCP context */
+       public String getObjectName() {
+               return null;
+       }
+
+       /** WARNING : do nothing in RCP context */
+       public void performFinish() {
+       }
+
+       /** WARNING : always return null in RCP context */
+       public InputStream getFileInputStream() {
+               return null;
+       }
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/OpenFile.java b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/OpenFile.java
new file mode 100644 (file)
index 0000000..610020d
--- /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.eclipse.ui.specific;
+
+import java.awt.Desktop;
+import java.io.File;
+import java.io.IOException;
+
+import org.argeo.ArgeoException;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+/**
+ * RCP specific command handler to open a file.
+ * 
+ * The parameter "URI" is used to determine the correct method to open it.
+ * 
+ * Various instances of this handler with different command ID might coexist in
+ * order to provide context specific open file service.
+ * 
+ */
+public class OpenFile extends AbstractHandler {
+       // private final static Log log = LogFactory.getLog(OpenFile.class);
+
+       public final static String PARAM_FILE_NAME = "param.fileName";
+       public final static String PARAM_FILE_URI = "param.fileURI";
+
+       private final static String FILE_SCHEME = "file";
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               @SuppressWarnings("unused")
+               @Deprecated
+               String fileName = event.getParameter(PARAM_FILE_NAME);
+               String fileUri = event.getParameter(PARAM_FILE_URI);
+
+               // sanity check
+               if (fileUri == null || "".equals(fileUri.trim()))
+                       return null;
+
+               Desktop desktop = null;
+               if (Desktop.isDesktopSupported()) {
+                       desktop = Desktop.getDesktop();
+               }
+
+               File file = getFileFromUri(fileUri);
+               if (file != null)
+                       try {
+                               desktop.open(file);
+                       } catch (IOException e) {
+                               throw new ArgeoException("Unable to open file with URI: "
+                                               + fileUri, e);
+                       }
+
+               return null;
+       }
+
+       protected File getFileFromUri(String uri) {
+               if (uri.startsWith(FILE_SCHEME)) {
+                       String path = uri.substring((FILE_SCHEME + "://").length());
+                       return new File(path);
+               }
+               return null;
+       }
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/ThreadNLS.java b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/ThreadNLS.java
new file mode 100644 (file)
index 0000000..c87f6e5
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.argeo.util.LocaleUtils;
+import org.eclipse.osgi.util.NLS;
+
+/** NLS attached to a given thread */
+public class ThreadNLS<T extends NLS> extends InheritableThreadLocal<T> {
+       public final static String DEFAULT_BUNDLE_LOCATION = "/properties/plugin";
+
+       private final String bundleLocation;
+
+       private Class<T> type;
+       private Boolean utf8 = false;
+
+       public ThreadNLS(String bundleLocation, Class<T> type, Boolean utf8) {
+               this.bundleLocation = bundleLocation;
+               this.type = type;
+               this.utf8 = utf8;
+       }
+
+       public ThreadNLS(Class<T> type) {
+               this(DEFAULT_BUNDLE_LOCATION, type, false);
+       }
+
+       @Override
+       protected T initialValue() {
+               ResourceBundle bundle = ResourceBundle.getBundle(bundleLocation,
+                               LocaleUtils.threadLocale.get(), type.getClassLoader());
+               T result;
+               try {
+                       NLS.initializeMessages(bundleLocation, type);
+                       Constructor<T> constructor = type.getConstructor();
+                       constructor.setAccessible(true);
+                       result = constructor.newInstance();
+                       final Field[] fieldArray = type.getDeclaredFields();
+                       for (int i = 0; i < fieldArray.length; i++) {
+                               int modifiers = fieldArray[i].getModifiers();
+                               if (String.class.isAssignableFrom(fieldArray[i].getType())
+                                               && Modifier.isPublic(modifiers)
+                                               && !Modifier.isStatic(modifiers)) {
+                                       try {
+                                               String value = bundle
+                                                               .getString(fieldArray[i].getName());
+                                               byte[] bytes = value.getBytes();
+
+                                               String forcedValue;
+                                               if (utf8)
+                                                       forcedValue = new String(bytes, "UTF8");
+                                               else
+                                                       forcedValue = value;
+                                               if (value != null) {
+                                                       fieldArray[i].setAccessible(true);
+                                                       fieldArray[i].set(result, forcedValue);
+                                               }
+                                       } catch (final MissingResourceException mre) {
+                                               fieldArray[i].setAccessible(true);
+                                               fieldArray[i].set(result, "");
+                                               mre.printStackTrace();
+                                       }
+                               }
+                       }
+                       return result;
+               } catch (final Exception ex) {
+                       throw new IllegalStateException(ex.getMessage());
+               }
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/UploadFileWizardPage.java b/trunk/base/runtime/org.argeo.eclipse.ui.rcp/src/main/java/org/argeo/eclipse/ui/specific/UploadFileWizardPage.java
new file mode 100644 (file)
index 0000000..5bdaece
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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.eclipse.ui.specific;
+
+import java.io.InputStream;
+
+import org.eclipse.jface.preference.DirectoryFieldEditor;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.widgets.Composite;
+
+public class UploadFileWizardPage extends WizardPage {
+
+       public final static String FILE_ITEM_TYPE = "FILE";
+       public final static String FOLDER_ITEM_TYPE = "FOLDER";
+
+       private DirectoryFieldEditor dfe;
+
+       public UploadFileWizardPage() {
+               super("Import from file system");
+               setDescription("Import files from the local file system into the JCR repository");
+       }
+
+       public void createControl(Composite parent) {
+               dfe = new DirectoryFieldEditor("directory", "From", parent);
+               setControl(dfe.getTextControl(parent));
+       }
+
+       public String getObjectPath() {
+               return dfe.getStringValue();
+       }
+
+       public String getObjectType() {
+               return FOLDER_ITEM_TYPE;
+       }
+
+       public boolean getNeedsProgressMonitor() {
+               return true;
+       }
+
+       // Dummy methods : useless in RCP context but useful for RAP
+       /** WARNING : always return null in RCP context */
+       public String getObjectName() {
+               return null;
+       }
+
+       /** WARNING : do nothing in RCP context */
+       public void performFinish() {
+       }
+
+       /** WARNING : always return null in RCP context */
+       public InputStream getFileInputStream() {
+               return null;
+       }
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/.classpath b/trunk/base/runtime/org.argeo.eclipse.ui/.classpath
new file mode 100644 (file)
index 0000000..a507434
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/.project b/trunk/base/runtime/org.argeo.eclipse.ui/.project
new file mode 100644 (file)
index 0000000..53e9b32
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.eclipse.ui</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/trunk/base/runtime/org.argeo.eclipse.ui/.settings/org.eclipse.jdt.core.prefs b/trunk/base/runtime/org.argeo.eclipse.ui/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..f441c90
--- /dev/null
@@ -0,0 +1,8 @@
+#Thu Jul 29 10:54:11 CEST 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/.settings/org.eclipse.pde.core.prefs b/trunk/base/runtime/org.argeo.eclipse.ui/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..8e36e33
--- /dev/null
@@ -0,0 +1,3 @@
+#Thu Jul 29 11:00:39 CEST 2010
+eclipse.preferences.version=1
+resolve.requirebundle=false
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/build.properties b/trunk/base/runtime/org.argeo.eclipse.ui/build.properties
new file mode 100644 (file)
index 0000000..5fc538b
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/pom.xml b/trunk/base/runtime/org.argeo.eclipse.ui/pom.xml
new file mode 100644 (file)
index 0000000..394bba4
--- /dev/null
@@ -0,0 +1,88 @@
+<?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.base</groupId>
+               <version>2.1.11</version>
+               <artifactId>runtime</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.eclipse.ui</artifactId>
+       <name>Commons Eclipse UI</name>
+       <packaging>jar</packaging>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-SymbolicName>${project.artifactId};singleton:=true</Bundle-SymbolicName>
+                                               <Bundle-ActivationPolicy>lazy</Bundle-ActivationPolicy>
+                                               <Bundle-Activator>org.argeo.eclipse.ui.ArgeoUiPlugin</Bundle-Activator>
+                                               <Require-Bundle>org.eclipse.ui;resolution:=optional,org.eclipse.rap.ui;resolution:=optional,org.eclipse.core.runtime</Require-Bundle>
+                                               <Import-Package>
+                                                       org.springframework.beans.factory,
+                                                       org.springframework.core.io.support,
+                                                       !org.eclipse.core.runtime,
+                                                       !org.eclipse.core.commands,
+                                                       !org.eclipse.ui.plugin,
+                                                       *
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- JCR -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.jcr</artifactId>
+               </dependency>
+
+               <!-- RCP only dependency, needed at compile time -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.dep.rcp</artifactId>
+                       <version>2.1.11</version>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- Commons -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.context</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.osgi.extender</artifactId>
+               </dependency>
+
+               <!-- Others -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/spring/ApplicationContextTracker.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/spring/ApplicationContextTracker.java
new file mode 100644 (file)
index 0000000..070c708
--- /dev/null
@@ -0,0 +1,150 @@
+/*\r
+ * Copyright (C) 2007-2012 Argeo GmbH\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *         http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.argeo.eclipse.spring;\r
+\r
+import static java.text.MessageFormat.format;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+import org.eclipse.core.runtime.Platform;\r
+import org.osgi.framework.Bundle;\r
+import org.osgi.framework.BundleContext;\r
+import org.osgi.framework.BundleException;\r
+import org.osgi.framework.FrameworkUtil;\r
+import org.osgi.framework.InvalidSyntaxException;\r
+import org.osgi.util.tracker.ServiceTracker;\r
+import org.springframework.context.ApplicationContext;\r
+\r
+/**\r
+ * Tracks Spring application context published as services.\r
+ * \r
+ * @author Heiko Seeberger\r
+ * @author Mathieu Baudier\r
+ */\r
+class ApplicationContextTracker {\r
+       private final static Log log = LogFactory\r
+                       .getLog(ApplicationContextTracker.class);\r
+\r
+       private static final String FILTER = "(&(objectClass=org.springframework.context.ApplicationContext)" //$NON-NLS-1$\r
+                       + "(org.springframework.context.service.name={0}))"; //$NON-NLS-1$\r
+\r
+       public final static String APPLICATION_CONTEXT_TRACKER_TIMEOUT = "org.argeo.eclipse.spring.applicationContextTrackerTimeout";\r
+\r
+       private static Long defaultTimeout = Long.parseLong(System.getProperty(\r
+                       APPLICATION_CONTEXT_TRACKER_TIMEOUT, "30000"));\r
+\r
+       private ServiceTracker applicationContextServiceTracker;\r
+\r
+       /**\r
+        * @param contributorBundle\r
+        *            OSGi bundle for which the Spring application context is to be\r
+        *            tracked. Must not be null!\r
+        * @param factoryBundleContext\r
+        *            BundleContext object which can be used to track services\r
+        * @throws IllegalArgumentException\r
+        *             if the given bundle is null.\r
+        */\r
+       public ApplicationContextTracker(final Bundle contributorBundle,\r
+                       final BundleContext factoryBundleContext) {\r
+               final String filter = format(FILTER,\r
+                               contributorBundle.getSymbolicName());\r
+               try {\r
+                       applicationContextServiceTracker = new ServiceTracker(\r
+                                       factoryBundleContext, FrameworkUtil.createFilter(filter),\r
+                                       null);\r
+                       // applicationContextServiceTracker.open();\r
+               } catch (final InvalidSyntaxException e) {\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+\r
+       public void open() {\r
+               if (applicationContextServiceTracker != null) {\r
+                       applicationContextServiceTracker.open();\r
+               }\r
+       }\r
+\r
+       public void close() {\r
+               if (applicationContextServiceTracker != null) {\r
+                       applicationContextServiceTracker.close();\r
+               }\r
+       }\r
+\r
+       public ApplicationContext getApplicationContext() {\r
+               ApplicationContext applicationContext = null;\r
+               if (applicationContextServiceTracker != null) {\r
+                       try {\r
+                               applicationContext = (ApplicationContext) applicationContextServiceTracker\r
+                                               .waitForService(defaultTimeout);\r
+                       } catch (InterruptedException e) {\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               return applicationContext;\r
+       }\r
+\r
+       @Override\r
+       protected void finalize() throws Throwable {\r
+               close();\r
+               super.finalize();\r
+       }\r
+\r
+       static ApplicationContext getApplicationContext(String bundleSymbolicName) {\r
+               Bundle contributorBundle = Platform.getBundle(bundleSymbolicName);\r
+               return getApplicationContext(contributorBundle);\r
+       }\r
+\r
+       static ApplicationContext getApplicationContext(\r
+                       final Bundle contributorBundle) {\r
+               if (log.isTraceEnabled())\r
+                       log.trace("Get application context for bundle " + contributorBundle);\r
+\r
+               // Start if not yet started (also if in STARTING state, may be lazy)\r
+               if (contributorBundle.getState() != Bundle.ACTIVE) {\r
+                       if (log.isTraceEnabled())\r
+                               log.trace("Starting bundle: "\r
+                                               + contributorBundle.getSymbolicName());\r
+                       // Thread startBundle = new Thread("Start bundle "\r
+                       // + contributorBundle.getSymbolicName()) {\r
+                       // public void run() {\r
+                       try {\r
+                               contributorBundle.start();\r
+                       } catch (BundleException e) {\r
+                               log.error("Cannot start bundle " + contributorBundle, e);\r
+                       }\r
+                       // }\r
+                       // };\r
+                       // startBundle.start();\r
+                       // try {\r
+                       // startBundle.join(10 * 1000l);\r
+                       // } catch (InterruptedException e) {\r
+                       // // silent\r
+                       // }\r
+               }\r
+\r
+               final ApplicationContextTracker applicationContextTracker = new ApplicationContextTracker(\r
+                               contributorBundle, contributorBundle.getBundleContext());\r
+               ApplicationContext applicationContext = null;\r
+               try {\r
+                       applicationContextTracker.open();\r
+                       applicationContext = applicationContextTracker\r
+                                       .getApplicationContext();\r
+               } finally {\r
+                       applicationContextTracker.close();\r
+               }\r
+               return applicationContext;\r
+       }\r
+}\r
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/spring/SpringCommandHandler.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/spring/SpringCommandHandler.java
new file mode 100644 (file)
index 0000000..4c7a152
--- /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.eclipse.spring;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.commands.IHandlerListener;
+import org.springframework.context.ApplicationContext;
+
+/** Allows to declare Eclipse commands as Spring beans */
+public class SpringCommandHandler implements IHandler {
+       private final static Log log = LogFactory
+                       .getLog(SpringCommandHandler.class);
+
+       public void addHandlerListener(IHandlerListener handlerListener) {
+       }
+
+       public void dispose() {
+       }
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               String commandId = event.getCommand().getId();
+               String bundleSymbolicName = commandId.substring(0,
+                               commandId.lastIndexOf('.'));
+               try {
+                       if (log.isTraceEnabled())
+                               log.trace("Execute " + event + " via spring command handler "
+                                               + this);
+                       // TODO: make it more flexible and robust
+                       ApplicationContext applicationContext = ApplicationContextTracker
+                                       .getApplicationContext(bundleSymbolicName);
+                       if (applicationContext == null)
+                               throw new ArgeoException("No application context found for "
+                                               + bundleSymbolicName);
+
+                       // retrieve the command via its id
+                       String beanName = event.getCommand().getId();
+
+                       if (!applicationContext.containsBean(beanName)) {
+                               if (beanName.startsWith(bundleSymbolicName))
+                                       beanName = beanName
+                                                       .substring(bundleSymbolicName.length() + 1);
+                       }
+
+                       if (!applicationContext.containsBean(beanName))
+                               throw new ExecutionException("No bean found with name "
+                                               + beanName + " in bundle " + bundleSymbolicName);
+                       Object bean = applicationContext.getBean(beanName);
+
+                       if (!(bean instanceof IHandler))
+                               throw new ExecutionException("Bean with name " + beanName
+                                               + " and class " + bean.getClass()
+                                               + " does not implement the IHandler interface.");
+
+                       IHandler handler = (IHandler) bean;
+                       return handler.execute(event);
+               } catch (Exception e) {
+                       // TODO: use eclipse error management
+                       // log.error(e);
+                       throw new ExecutionException("Cannot execute Spring command "
+                                       + commandId + " in bundle " + bundleSymbolicName, e);
+               }
+       }
+
+       public boolean isEnabled() {
+               return true;
+       }
+
+       public boolean isHandled() {
+               return true;
+       }
+
+       public void removeHandlerListener(IHandlerListener handlerListener) {
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/spring/SpringExtensionFactory.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/spring/SpringExtensionFactory.java
new file mode 100644 (file)
index 0000000..7a0486f
--- /dev/null
@@ -0,0 +1,114 @@
+/*\r
+ * Copyright (C) 2007-2012 Argeo GmbH\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *         http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.argeo.eclipse.spring;\r
+\r
+import org.argeo.ArgeoException;\r
+import org.eclipse.core.runtime.CoreException;\r
+import org.eclipse.core.runtime.IConfigurationElement;\r
+import org.eclipse.core.runtime.IExecutableExtension;\r
+import org.eclipse.core.runtime.IExecutableExtensionFactory;\r
+import org.eclipse.core.runtime.IExtension;\r
+import org.springframework.context.ApplicationContext;\r
+\r
+/**\r
+ * The Spring Extension Factory builds a bridge between the Eclipse Extension\r
+ * Registry and the Spring Framework (especially Spring Dynamic Modules).\r
+ * \r
+ * It allows you to define your extension as a spring bean within the spring\r
+ * application context of your bundle. If you would like to use this bean as an\r
+ * instance of an extension (an Eclipse RCP view, for example) you define the\r
+ * extension with this spring extension factory as the class to be created.\r
+ * \r
+ * To let the spring extension factory pick the right bean from your application\r
+ * context you need to set the bean id to the same value as the id of the view\r
+ * within the view definition, for example. This is important if your extension\r
+ * definition contains more than one element, where each element has its own id.\r
+ * \r
+ * If the extension definition elements themselves have no id attribute the\r
+ * spring extension factory uses the id of the extension itself to identify the\r
+ * bean.\r
+ * \r
+ * original code from: <a href=\r
+ * "http://martinlippert.blogspot.com/2008/10/new-version-of-spring-extension-factory.html"\r
+ * >Blog entry</a>\r
+ * \r
+ * @author Martin Lippert\r
+ * @author mbaudier\r
+ */\r
+public class SpringExtensionFactory implements IExecutableExtensionFactory,\r
+               IExecutableExtension {\r
+\r
+       private Object bean;\r
+\r
+       public Object create() throws CoreException {\r
+               if (bean == null)\r
+                       throw new ArgeoException("No underlying bean for extension");\r
+               return bean;\r
+       }\r
+\r
+       public void setInitializationData(IConfigurationElement config,\r
+                       String propertyName, Object data) throws CoreException {\r
+               String bundleSymbolicName = config.getContributor().getName();\r
+               ApplicationContext applicationContext = ApplicationContextTracker\r
+                               .getApplicationContext(bundleSymbolicName);\r
+               if (applicationContext == null)\r
+                       throw new ArgeoException(\r
+                                       "Cannot find application context for bundle "\r
+                                                       + bundleSymbolicName);\r
+\r
+               String beanName = getBeanName(data, config);\r
+               if (beanName == null)\r
+                       throw new ArgeoException("Cannot find bean name for extension "\r
+                                       + config);\r
+\r
+               if (!applicationContext.containsBean(beanName)) {\r
+                       if (beanName.startsWith(bundleSymbolicName))\r
+                               beanName = beanName.substring(bundleSymbolicName.length() + 1);\r
+               }\r
+\r
+               if (!applicationContext.containsBean(beanName))\r
+                       throw new ArgeoException("No bean with name '" + beanName + "'");\r
+\r
+               this.bean = applicationContext.getBean(beanName);\r
+               if (this.bean instanceof IExecutableExtension) {\r
+                       ((IExecutableExtension) this.bean).setInitializationData(config,\r
+                                       propertyName, data);\r
+               }\r
+       }\r
+\r
+       private String getBeanName(Object data, IConfigurationElement config) {\r
+\r
+               // try the specific bean id the extension defines\r
+               if (data != null && data.toString().length() > 0) {\r
+                       return data.toString();\r
+               }\r
+\r
+               // try the id of the config element\r
+               if (config.getAttribute("id") != null) {\r
+                       return config.getAttribute("id");\r
+               }\r
+\r
+               // try the id of the extension element itself\r
+               if (config.getParent() != null\r
+                               && config.getParent() instanceof IExtension) {\r
+                       IExtension extensionDefinition = (IExtension) config.getParent();\r
+                       return extensionDefinition.getSimpleIdentifier();\r
+               }\r
+\r
+               return null;\r
+       }\r
+\r
+}\r
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/AbstractTreeContentProvider.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/AbstractTreeContentProvider.java
new file mode 100644 (file)
index 0000000..6b86676
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.eclipse.ui;
+
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Tree content provider dealing with tree objects and providing reasonable
+ * defaults.
+ */
+public abstract class AbstractTreeContentProvider implements
+               ITreeContentProvider {
+       private static final long serialVersionUID = 8246126401957763868L;
+
+       /** Does nothing */
+       public void dispose() {
+       }
+
+       /** Does nothing */
+       public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+       }
+
+       public Object[] getChildren(Object element) {
+               if (element instanceof TreeParent) {
+                       return ((TreeParent) element).getChildren();
+               }
+               return new Object[0];
+       }
+
+       public Object getParent(Object element) {
+               if (element instanceof TreeParent) {
+                       return ((TreeParent) element).getParent();
+               }
+               return null;
+       }
+
+       public boolean hasChildren(Object element) {
+               if (element instanceof TreeParent) {
+                       return ((TreeParent) element).hasChildren();
+               }
+               return false;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/ArgeoUiPlugin.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/ArgeoUiPlugin.java
new file mode 100644 (file)
index 0000000..6befdf7
--- /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.eclipse.ui;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.core.runtime.ILogListener;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class ArgeoUiPlugin extends AbstractUIPlugin implements ILogListener {
+       public static final String PLUGIN_ID = "org.argeo.eclipse.ui";
+       private final static Log log = LogFactory.getLog(ArgeoUiPlugin.class);
+       // The shared instance
+       private static ArgeoUiPlugin plugin;
+
+       public void start(BundleContext context) throws Exception {
+               super.start(context);
+               // weirdly, the start method is called twice...
+               if (plugin == null) {
+                       plugin = this;
+                       Platform.addLogListener(this);
+                       log.debug("Eclipse logging now directed to standard logging");
+               }
+       }
+
+       public void stop(BundleContext context) throws Exception {
+               try {
+                       // weirdly, the stop method is called twice...
+                       if (plugin != null) {
+                               Platform.removeLogListener(this);
+                               log.debug("Eclipse logging not directed anymore to standard logging");
+                               plugin = null;
+                       }
+               } finally {
+                       super.stop(context);
+               }
+       }
+
+       /** Returns the shared instance */
+       public static ArgeoUiPlugin getDefault() {
+               return plugin;
+       }
+
+       public void logging(IStatus status, String plugin) {
+               Log pluginLog = LogFactory.getLog(plugin);
+               Integer severity = status.getSeverity();
+               if (severity == IStatus.ERROR)
+                       pluginLog.error(status.getMessage(), status.getException());
+               else if (severity == IStatus.WARNING)
+                       pluginLog.warn(status.getMessage(), status.getException());
+               else if (severity == IStatus.INFO)
+                       pluginLog.info(status.getMessage(), status.getException());
+               else if (severity == IStatus.CANCEL)
+                       if (pluginLog.isDebugEnabled())
+                               pluginLog.debug(status.getMessage(), status.getException());
+
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/ColumnViewerComparator.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/ColumnViewerComparator.java
new file mode 100644 (file)
index 0000000..8db5d4f
--- /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.eclipse.ui;
+
+import java.util.Comparator;
+
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+/** Generic column viewer sorter */
+public class ColumnViewerComparator<T> extends ViewerComparator {
+       private static final long serialVersionUID = -2266218906355859909L;
+
+       public static final int ASC = 1;
+
+       public static final int NONE = 0;
+
+       public static final int DESC = -1;
+
+       private int direction = 0;
+
+       private TableViewerColumn column;
+
+       private ColumnViewer viewer;
+
+       public ColumnViewerComparator(TableViewerColumn column,
+                       Comparator<T> comparator) {
+               super(comparator);
+               this.column = column;
+               this.viewer = column.getViewer();
+               this.column.getColumn().addSelectionListener(new SelectionAdapter() {
+                       private static final long serialVersionUID = 7586796298965472189L;
+
+                       public void widgetSelected(SelectionEvent e) {
+                               if (ColumnViewerComparator.this.viewer.getComparator() != null) {
+                                       if (ColumnViewerComparator.this.viewer.getComparator() == ColumnViewerComparator.this) {
+                                               int tdirection = ColumnViewerComparator.this.direction;
+
+                                               if (tdirection == ASC) {
+                                                       setSortDirection(DESC);
+                                               } else if (tdirection == DESC) {
+                                                       setSortDirection(NONE);
+                                               }
+                                       } else {
+                                               setSortDirection(ASC);
+                                       }
+                               } else {
+                                       setSortDirection(ASC);
+                               }
+                       }
+               });
+       }
+
+       private void setSortDirection(int direction) {
+               if (direction == NONE) {
+                       column.getColumn().getParent().setSortColumn(null);
+                       column.getColumn().getParent().setSortDirection(SWT.NONE);
+                       viewer.setComparator(null);
+               } else {
+                       column.getColumn().getParent().setSortColumn(column.getColumn());
+                       this.direction = direction;
+
+                       if (direction == ASC) {
+                               column.getColumn().getParent().setSortDirection(SWT.DOWN);
+                       } else {
+                               column.getColumn().getParent().setSortDirection(SWT.UP);
+                       }
+
+                       if (viewer.getComparator() == this) {
+                               viewer.refresh();
+                       } else {
+                               viewer.setComparator(this);
+                       }
+
+               }
+       }
+
+       @SuppressWarnings("unchecked")
+       public int compare(Viewer viewer, Object e1, Object e2) {
+               return direction * getComparator().compare((T) e1, (T) e2);
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/EclipseArgeoMonitor.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/EclipseArgeoMonitor.java
new file mode 100644 (file)
index 0000000..7c0b398
--- /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.eclipse.ui;
+
+import org.argeo.ArgeoMonitor;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Wraps an Eclipse {@link IProgressMonitor} so that it can be passed to
+ * framework agnostic Argeo routines.
+ */
+public class EclipseArgeoMonitor implements ArgeoMonitor {
+       private final IProgressMonitor progressMonitor;
+
+       public EclipseArgeoMonitor(IProgressMonitor progressMonitor) {
+               this.progressMonitor = progressMonitor;
+       }
+
+       public void beginTask(String name, int totalWork) {
+               progressMonitor.beginTask(name, totalWork);
+       }
+
+       public void done() {
+               progressMonitor.done();
+       }
+
+       public boolean isCanceled() {
+               return progressMonitor.isCanceled();
+       }
+
+       public void setCanceled(boolean value) {
+               progressMonitor.setCanceled(value);
+       }
+
+       public void setTaskName(String name) {
+               progressMonitor.setTaskName(name);
+       }
+
+       public void subTask(String name) {
+               progressMonitor.subTask(name);
+       }
+
+       public void worked(int work) {
+               progressMonitor.worked(work);
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/EclipseUiUtils.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/EclipseUiUtils.java
new file mode 100644 (file)
index 0000000..8e1c7e6
--- /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.eclipse.ui;
+
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Utilities to simplify UI development. */
+public class EclipseUiUtils {
+       /**
+        * Create a label and a text field for a grid layout, the text field grabing
+        * excess horizontal
+        * 
+        * @param parent
+        *            the parent composite
+        * @param label
+        *            the lable to display
+        * @param modifyListener
+        *            a {@link ModifyListener} to listen on events on the text, can
+        *            be null
+        * @return the created text
+        * 
+        */
+       // FIXME why was this deprecated.
+       // * @ deprecated use { @ link #createGridLT(Composite, String)} instead
+       // @ Deprecated
+       public static Text createGridLT(Composite parent, String label,
+                       ModifyListener modifyListener) {
+               Label lbl = new Label(parent, SWT.LEAD);
+               lbl.setText(label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Text txt = new Text(parent, SWT.LEAD | SWT.BORDER);
+               txt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+               if (modifyListener != null)
+                       txt.addModifyListener(modifyListener);
+               return txt;
+       }
+
+       /**
+        * Create a label and a text field for a grid layout, the text field
+        * grabbing excess horizontal
+        */
+       public static Text createGridLT(Composite parent, String label) {
+               return createGridLT(parent, label, null);
+       }
+
+       /**
+        * Creates one label and a text field not editable with background color of
+        * the parent (like a label but with selectable text)
+        */
+       public static Text createGridLL(Composite parent, String label, String text) {
+               Text txt = createGridLT(parent, label);
+               txt.setText(text);
+               txt.setEditable(false);
+               txt.setBackground(parent.getBackground());
+               return txt;
+       }
+
+       /**
+        * Create a label and a text field with password display for a grid layout,
+        * the text field grabbing excess horizontal
+        */
+       public static Text createGridLP(Composite parent, String label,
+                       ModifyListener modifyListener) {
+               Label lbl = new Label(parent, SWT.LEAD);
+               lbl.setText(label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Text txt = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.PASSWORD);
+               txt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+               if (txt != null)
+                       txt.addModifyListener(modifyListener);
+               return txt;
+       }
+
+       /** Shortcut to retrieve default italic font from display */
+       public static Font getItalicFont(Composite parent) {
+               return JFaceResources.getFontRegistry().defaultFontDescriptor()
+                               .setStyle(SWT.ITALIC).createFont(parent.getDisplay());
+       }
+
+       /** Shortcut to retrieve default bold font from display */
+       public static Font getBoldFont(Composite parent) {
+               return JFaceResources.getFontRegistry().defaultFontDescriptor()
+                               .setStyle(SWT.BOLD).createFont(parent.getDisplay());
+       }
+
+       /** Shortcut to retrieve default bold italic font from display */
+       public static Font getBoldItalicFont(Composite parent) {
+               return JFaceResources.getFontRegistry().defaultFontDescriptor()
+                               .setStyle(SWT.BOLD | SWT.ITALIC)
+                               .createFont(parent.getDisplay());
+       }
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/Error.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/Error.java
new file mode 100644 (file)
index 0000000..03b0470
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.eclipse.ui;
+
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @deprecated deprecated because of poor naming, use {@link ErrorFeedback}
+ *             instead
+ */
+@SuppressWarnings("serial")
+@Deprecated
+public class Error extends ErrorFeedback {
+
+       public Error(Shell parentShell, String message, Throwable e) {
+               super(parentShell, message, e);
+               // TODO Auto-generated constructor stub
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/ErrorFeedback.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/ErrorFeedback.java
new file mode 100644 (file)
index 0000000..895e8b8
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.eclipse.ui;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+/** Generic error dialog to be used in try/catch blocks */
+@SuppressWarnings("serial")
+public class ErrorFeedback extends TitleAreaDialog {
+       private final static Log log = LogFactory.getLog(ErrorFeedback.class);
+
+       private final String message;
+       private final Throwable exception;
+
+       public static void show(String message, Throwable e) {
+               // rethrow ThreaDeath in order to make sure that RAP will properly clean
+               // up the UI thread
+               if (e instanceof ThreadDeath)
+                       throw (ThreadDeath) e;
+
+               new ErrorFeedback(getDisplay().getActiveShell(), message, e).open();
+       }
+
+       public static void show(String message) {
+               new ErrorFeedback(getDisplay().getActiveShell(), message, null).open();
+       }
+
+       /** Tries to find a display */
+       private static Display getDisplay() {
+               try {
+                       Display display = PlatformUI.getWorkbench().getDisplay();
+                       if (display != null)
+                               return display;
+                       else
+                               return Display.getDefault();
+               } catch (Exception e) {
+                       return Display.getCurrent();
+               }
+       }
+
+       public ErrorFeedback(Shell parentShell, String message, Throwable e) {
+               super(parentShell);
+               this.message = message;
+               this.exception = e;
+               log.error(message, e);
+       }
+
+       protected Point getInitialSize() {
+               if (exception != null)
+                       return new Point(800, 600);
+               else
+                       return new Point(400, 300);
+       }
+
+       @Override
+       protected Control createDialogArea(Composite parent) {
+               Composite dialogarea = (Composite) super.createDialogArea(parent);
+               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               Composite composite = new Composite(dialogarea, SWT.NONE);
+               composite.setLayout(new GridLayout(2, false));
+               composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               setMessage(message != null ? message
+                               + (exception != null ? ": " + exception.getMessage() : "")
+                               : exception != null ? exception.getMessage() : "Unkown Error",
+                               IMessageProvider.ERROR);
+
+               if (exception != null) {
+                       Text stack = new Text(composite, SWT.MULTI | SWT.LEAD | SWT.BORDER
+                                       | SWT.V_SCROLL | SWT.H_SCROLL);
+                       stack.setEditable(false);
+                       stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       StringWriter sw = new StringWriter();
+                       exception.printStackTrace(new PrintWriter(sw));
+                       stack.setText(sw.toString());
+               }
+
+               parent.pack();
+               return composite;
+       }
+
+       protected void configureShell(Shell shell) {
+               super.configureShell(shell);
+               shell.setText("Error");
+       }
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/GenericTableComparator.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/GenericTableComparator.java
new file mode 100644 (file)
index 0000000..a4179cb
--- /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.eclipse.ui;
+
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+
+public abstract class GenericTableComparator extends ViewerComparator {
+       private static final long serialVersionUID = -1175894935075325810L;
+       protected int propertyIndex;
+       public static final int ASCENDING = 0, DESCENDING = 1;
+       protected int direction = DESCENDING;
+
+       /**
+        * Creates an instance of a sorter for TableViewer.
+        * 
+        * @param defaultColumn
+        *            the default sorter column
+        */
+
+       public GenericTableComparator(int defaultColumnIndex, int direction) {
+               propertyIndex = defaultColumnIndex;
+               this.direction = direction;
+       }
+
+       public void setColumn(int column) {
+               if (column == this.propertyIndex) {
+                       // Same column as last sort; toggle the direction
+                       direction = 1 - direction;
+               } else {
+                       // New column; do a descending sort
+                       this.propertyIndex = column;
+                       direction = DESCENDING;
+               }
+       }
+
+       /**
+        * Must be Overriden in each view.
+        */
+       public abstract int compare(Viewer viewer, Object e1, Object e2);
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/TreeParent.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/TreeParent.java
new file mode 100644 (file)
index 0000000..2dfd2e6
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * 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.eclipse.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Parent / children semantic to be used for simple UI Tree structure */
+public class TreeParent {
+       private String name;
+       private TreeParent parent;
+
+       private List<Object> children;
+
+       /**
+        * Unique id within the context of a tree display. If set, equals() and
+        * hashCode() methods will be based on it
+        */
+       private String path = null;
+
+       /** False until at least one child has been added, then true until cleared */
+       private boolean loaded = false;
+
+       public TreeParent(String name) {
+               this.name = name;
+               children = new ArrayList<Object>();
+       }
+
+       public synchronized void addChild(Object child) {
+               loaded = true;
+               children.add(child);
+               if (child instanceof TreeParent)
+                       ((TreeParent) child).setParent(this);
+       }
+
+       /**
+        * Remove this child. The child is disposed.
+        */
+       public synchronized void removeChild(Object child) {
+               children.remove(child);
+               if (child instanceof TreeParent) {
+                       ((TreeParent) child).dispose();
+               }
+       }
+
+       public synchronized void clearChildren() {
+               for (Object obj : children) {
+                       if (obj instanceof TreeParent)
+                               ((TreeParent) obj).dispose();
+               }
+               loaded = false;
+               children.clear();
+       }
+
+       /**
+        * If overridden, <code>super.dispose()</code> must be called, typically
+        * after custom cleaning.
+        */
+       public synchronized void dispose() {
+               clearChildren();
+               parent = null;
+               children = null;
+       }
+
+       public synchronized Object[] getChildren() {
+               return children.toArray(new Object[children.size()]);
+       }
+
+       @SuppressWarnings("unchecked")
+       public synchronized <T> List<T> getChildrenOfType(Class<T> clss) {
+               List<T> lst = new ArrayList<T>();
+               for (Object obj : children) {
+                       if (clss.isAssignableFrom(obj.getClass()))
+                               lst.add((T) obj);
+               }
+               return lst;
+       }
+
+       public synchronized boolean hasChildren() {
+               return children.size() > 0;
+       }
+
+       public Object getChildByName(String name) {
+               for (Object child : children) {
+                       if (child.toString().equals(name))
+                               return child;
+               }
+               return null;
+       }
+
+       public synchronized Boolean isLoaded() {
+               return loaded;
+       }
+
+       public String getName() {
+               return name;
+       }
+
+       public void setParent(TreeParent parent) {
+               this.parent = parent;
+               if (parent != null && parent.path != null)
+                       this.path = parent.path + '/' + name;
+               else
+                       this.path = '/' + name;
+       }
+
+       public TreeParent getParent() {
+               return parent;
+       }
+
+       public String toString() {
+               return getName();
+       }
+
+       public int compareTo(TreeParent o) {
+               return name.compareTo(o.name);
+       }
+
+       @Override
+       public int hashCode() {
+               if (path != null)
+                       return path.hashCode();
+               else
+                       return name.hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (path != null && obj instanceof TreeParent)
+                       return path.equals(((TreeParent) obj).path);
+               else
+                       return name.equals(obj.toString());
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/dialogs/Error.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/dialogs/Error.java
new file mode 100644 (file)
index 0000000..918bfd2
--- /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.eclipse.ui.dialogs;
+
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Generic error dialog to be used in try/catch blocks
+ * 
+ * @deprecated use {@link org.argeo.eclipse.ui.ErrorFeedback} instead.
+ */
+public class Error extends org.argeo.eclipse.ui.ErrorFeedback {
+       private static final long serialVersionUID = -93864960090248736L;
+
+       public Error(Shell parentShell, String message, Throwable e) {
+               super(parentShell, message, e);
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/dialogs/SingleValue.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/dialogs/SingleValue.java
new file mode 100644 (file)
index 0000000..b58f446
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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.eclipse.ui.dialogs;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/** Dialog retrieve a single value. */
+public class SingleValue extends TitleAreaDialog {
+       private static final long serialVersionUID = 2843538207460082349L;
+
+       private Text valueT;
+       private String value;
+       private final String title, message, label;
+       private final Boolean multiline;
+
+       public static String ask(String label, String message) {
+               SingleValue svd = new SingleValue(label, message);
+               if (svd.open() == Dialog.OK)
+                       return svd.getString();
+               else
+                       return null;
+       }
+
+       public static Long askLong(String label, String message) {
+               SingleValue svd = new SingleValue(label, message);
+               if (svd.open() == Dialog.OK)
+                       return svd.getLong();
+               else
+                       return null;
+       }
+
+       public static Double askDouble(String label, String message) {
+               SingleValue svd = new SingleValue(label, message);
+               if (svd.open() == Dialog.OK)
+                       return svd.getDouble();
+               else
+                       return null;
+       }
+
+       public SingleValue(String label, String message) {
+               this(Display.getDefault().getActiveShell(), label, message, label,
+                               false);
+       }
+
+       public SingleValue(Shell parentShell, String title, String message,
+                       String label, Boolean multiline) {
+               super(parentShell);
+               this.title = title;
+               this.message = message;
+               this.label = label;
+               this.multiline = multiline;
+       }
+
+       protected Point getInitialSize() {
+               return new Point(300, 250);
+       }
+
+       protected Control createDialogArea(Composite parent) {
+               Composite dialogarea = (Composite) super.createDialogArea(parent);
+               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               Composite composite = new Composite(dialogarea, SWT.NONE);
+               composite.setLayout(new GridLayout(2, false));
+               composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               valueT = createLT(composite, label);
+
+               setMessage(message, IMessageProvider.NONE);
+
+               parent.pack();
+               return composite;
+       }
+
+       @Override
+       protected void okPressed() {
+               value = valueT.getText();
+               super.okPressed();
+       }
+
+       /** Creates label and text. */
+       protected Text createLT(Composite parent, String label) {
+               new Label(parent, SWT.NONE).setText(label);
+               Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER
+                               | (multiline ? SWT.MULTI : SWT.NONE));
+               text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               return text;
+       }
+
+       protected void configureShell(Shell shell) {
+               super.configureShell(shell);
+               shell.setText(title);
+       }
+
+       public String getString() {
+               return value;
+       }
+
+       public Long getLong() {
+               return Long.valueOf(getString());
+       }
+
+       public Double getDouble() {
+               return Double.valueOf(getString());
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/utils/CommandUtils.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/utils/CommandUtils.java
new file mode 100644 (file)
index 0000000..22a139f
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * 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.eclipse.ui.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.ArgeoUiPlugin;
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.IParameter;
+import org.eclipse.core.commands.Parameterization;
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.SWT;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.handlers.IHandlerService;
+import org.eclipse.ui.menus.CommandContributionItem;
+import org.eclipse.ui.menus.CommandContributionItemParameter;
+import org.eclipse.ui.services.IServiceLocator;
+
+/**
+ * Centralises useful and generic methods when dealing with commands in an
+ * Eclipse Workbench context
+ */
+public class CommandUtils {
+
+       /**
+        * Commodities the refresh of a single command with no parameter in a
+        * Menu.aboutToShow method to simplify further development
+        * 
+        * Note: that this method should be called with a false show command flag to
+        * remove a contribution that have been previously contributed
+        * 
+        * @param menuManager
+        * @param locator
+        * @param cmdId
+        * @param label
+        * @param icon
+        * @param showCommand
+        */
+       public static void refreshCommand(IMenuManager menuManager,
+                       IServiceLocator locator, String cmdId, String label,
+                       ImageDescriptor icon, boolean showCommand) {
+               refreshParameterizedCommand(menuManager, locator, cmdId, label, icon,
+                               showCommand, null);
+       }
+
+       /**
+        * Commodities the refresh the contribution of a command with a map of
+        * parameters in a context menu
+        * 
+        * The command ID is used has contribution item ID
+        * 
+        * @param menuManager
+        * @param locator
+        * @param cmdId
+        * @param label
+        * @param iconPath
+        * @param showCommand
+        */
+       public static void refreshParameterizedCommand(IMenuManager menuManager,
+                       IServiceLocator locator, String cmdId, String label,
+                       ImageDescriptor icon, boolean showCommand,
+                       Map<String, String> params) {
+               refreshParameterizedCommand(menuManager, locator, cmdId, cmdId, label,
+                               icon, showCommand, params);
+       }
+
+       /**
+        * Commodities the refresh the contribution of a command with a map of
+        * parameters in a context menu
+        * 
+        * @param menuManager
+        * @param locator
+        * @param contributionId
+        * @param commandId
+        * @param label
+        * @param icon
+        * @param showCommand
+        * @param params
+        */
+       public static void refreshParameterizedCommand(IMenuManager menuManager,
+                       IServiceLocator locator, String contributionId, String commandId,
+                       String label, ImageDescriptor icon, boolean showCommand,
+                       Map<String, String> params) {
+               IContributionItem ici = menuManager.find(contributionId);
+               if (ici != null)
+                       menuManager.remove(ici);
+               if (showCommand) {
+                       CommandContributionItemParameter contributionItemParameter = new CommandContributionItemParameter(
+                                       locator, null, commandId, SWT.PUSH);
+
+                       // Set Params
+                       contributionItemParameter.label = label;
+                       contributionItemParameter.icon = icon;
+
+                       if (params != null)
+                               contributionItemParameter.parameters = params;
+
+                       CommandContributionItem cci = new CommandContributionItem(
+                                       contributionItemParameter);
+                       cci.setId(contributionId);
+                       menuManager.add(cci);
+               }
+       }
+
+       /** Helper to call a command without parameter easily */
+       public static void callCommand(String commandID) {
+               callCommand(commandID, null);
+       }
+
+       /** Helper to call a command with a single parameter easily */
+       public static void callCommand(String commandID, String parameterID,
+                       String parameterValue) {
+               Map<String, String> params = new HashMap<String, String>();
+               params.put(parameterID, parameterValue);
+               callCommand(commandID, params);
+       }
+
+       /**
+        * Helper to call a command with a map of parameters easily
+        * 
+        * @param paramMap
+        *            a map that links various command IDs with corresponding String
+        *            values.
+        */
+       public static void callCommand(String commandID,
+                       Map<String, String> paramMap) {
+               try {
+                       IWorkbench iw = ArgeoUiPlugin.getDefault().getWorkbench();
+                       IHandlerService handlerService = (IHandlerService) iw
+                                       .getService(IHandlerService.class);
+                       ICommandService cmdService = (ICommandService) iw
+                                       .getActiveWorkbenchWindow().getService(
+                                                       ICommandService.class);
+                       Command cmd = cmdService.getCommand(commandID);
+
+                       ArrayList<Parameterization> parameters = null;
+                       ParameterizedCommand pc;
+
+                       if (paramMap != null) {
+                               // Set parameters of the command to launch :
+                               parameters = new ArrayList<Parameterization>();
+                               Parameterization parameterization;
+
+                               for (String id : paramMap.keySet()) {
+                                       parameterization = new Parameterization(
+                                                       cmd.getParameter(id), paramMap.get(id));
+                                       parameters.add(parameterization);
+                               }
+                               pc = new ParameterizedCommand(cmd,
+                                               parameters.toArray(new Parameterization[parameters
+                                                               .size()]));
+                       } else
+                               pc = new ParameterizedCommand(cmd, null);
+
+                       // execute the command
+                       handlerService.executeCommand(pc, null);
+               } catch (Exception e) {
+                       throw new ArgeoException("Unexpected error while"
+                                       + " calling the command " + commandID, e);
+               }
+       }
+
+       // legacy methods. Should be removed soon
+
+       /**
+        * Shortcut to call a command with a single parameter.
+        * 
+        * WARNING: none of the parameter can be null
+        * 
+        * @deprecated rather use <code>callCommand(commandID,parameterID,
+                       parameterValue)</code>
+        */
+       public static void CallCommandWithOneParameter(String commandId,
+                       String paramId, String paramValue) {
+               try {
+                       IWorkbench iw = ArgeoUiPlugin.getDefault().getWorkbench();
+                       IHandlerService handlerService = (IHandlerService) iw
+                                       .getService(IHandlerService.class);
+
+                       // Gets a command that must have been previously registered
+                       IWorkbenchWindow window = iw.getActiveWorkbenchWindow();
+                       ICommandService cmdService = (ICommandService) window
+                                       .getService(ICommandService.class);
+                       Command cmd = cmdService.getCommand(commandId);
+
+                       // Manages the single parameter
+                       ArrayList<Parameterization> parameters = new ArrayList<Parameterization>();
+                       IParameter iparam = cmd.getParameter(paramId);
+                       Parameterization params = new Parameterization(iparam, paramValue);
+                       parameters.add(params);
+
+                       // Create and execute the command
+                       ParameterizedCommand pc = new ParameterizedCommand(cmd,
+                                       parameters.toArray(new Parameterization[parameters.size()]));
+                       handlerService = (IHandlerService) window
+                                       .getService(IHandlerService.class);
+                       handlerService.executeCommand(pc, null);
+               } catch (Exception e) {
+                       throw new ArgeoException(
+                                       "Error calling command of id:" + commandId, e);
+               }
+       }
+
+       /**
+        * Commodities the refresh of a single command with a map of parameters in a
+        * Menu.aboutToShow method to simplify further development Rather use
+        * {@link refreshParameterizedCommand()}
+        */
+       @Deprecated
+       public static void refreshParametrizedCommand(IMenuManager menuManager,
+                       IServiceLocator locator, String cmdId, String label,
+                       ImageDescriptor icon, boolean showCommand,
+                       Map<String, String> params) {
+               refreshParameterizedCommand(menuManager, locator, cmdId, label, icon,
+                               showCommand, params);
+       }
+}
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/utils/ViewerUtils.java b/trunk/base/runtime/org.argeo.eclipse.ui/src/main/java/org/argeo/eclipse/ui/utils/ViewerUtils.java
new file mode 100644 (file)
index 0000000..42e9fab
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.eclipse.ui.utils;
+
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.TreeViewerColumn;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TreeColumn;
+
+/**
+ * Centralizes useful methods to manage Jface Table, Tree and TreeColumn
+ * viewers.
+ */
+public class ViewerUtils {
+
+       /**
+        * Creates a basic column for the given table. For the time being, we do not
+        * support moveable columns.
+        */
+       public static TableColumn createColumn(Table parent, String name,
+                       int style, int width) {
+               TableColumn result = new TableColumn(parent, style);
+               result.setText(name);
+               result.setWidth(width);
+               result.setResizable(true);
+               return result;
+       }
+
+       /**
+        * Creates a TableViewerColumn for the given viewer. For the time being, we
+        * do not support moveable columns.
+        */
+       public static TableViewerColumn createTableViewerColumn(TableViewer parent,
+                       String name, int style, int width) {
+               TableViewerColumn tvc = new TableViewerColumn(parent, style);
+               final TableColumn column = tvc.getColumn();
+               column.setText(name);
+               column.setWidth(width);
+               column.setResizable(true);
+               return tvc;
+       }
+
+       /**
+        * Creates a TreeViewerColumn for the given viewer. For the time being, we
+        * do not support moveable columns.
+        */
+       public static TreeViewerColumn createTreeViewerColumn(TreeViewer parent,
+                       String name, int style, int width) {
+               TreeViewerColumn tvc = new TreeViewerColumn(parent, style);
+               final TreeColumn column = tvc.getColumn();
+               column.setText(name);
+               column.setWidth(width);
+               column.setResizable(true);
+               return tvc;
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/.classpath b/trunk/base/runtime/org.argeo.osgi.boot/.classpath
new file mode 100644 (file)
index 0000000..2f7e966
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+       <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+       <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/J2SE-1.4"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/.project b/trunk/base/runtime/org.argeo.osgi.boot/.project
new file mode 100644 (file)
index 0000000..e145e96
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.osgi.boot</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>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.eclipse.pde.PluginNature</nature>
+       </natures>
+</projectDescription>
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/.settings/org.eclipse.jdt.core.prefs b/trunk/base/runtime/org.argeo.osgi.boot/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..ba812fb
--- /dev/null
@@ -0,0 +1,12 @@
+#Fri Jun 26 11:15:56 CEST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.source=1.3
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/build.properties b/trunk/base/runtime/org.argeo.osgi.boot/build.properties
new file mode 100644 (file)
index 0000000..f17a582
--- /dev/null
@@ -0,0 +1 @@
+additional.bundles = junit
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/pom.xml b/trunk/base/runtime/org.argeo.osgi.boot/pom.xml
new file mode 100644 (file)
index 0000000..3fb8711
--- /dev/null
@@ -0,0 +1,53 @@
+<?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.base</groupId>
+               <version>2.1.11</version>
+               <artifactId>runtime</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.osgi.boot</artifactId>
+       <packaging>jar</packaging>
+       <name>Commons OSGi Boot</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                               <configuration>
+                                       <source>1.4</source>
+                                       <target>1.4</target>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-Activator>org.argeo.osgi.boot.Activator</Bundle-Activator>
+                                               <Bundle-RequiredExecutionEnvironment>J2SE-1.4</Bundle-RequiredExecutionEnvironment>
+                                               <Import-Package>org.eclipse.*;resolution:=optional,*</Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.tp.rap.platform</groupId>
+                       <artifactId>org.eclipse.osgi</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- TEST -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>junit</artifactId>
+                       <scope>test</scope>
+               </dependency>
+       </dependencies>
+
+
+</project>
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/Activator.java b/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/Activator.java
new file mode 100644 (file)
index 0000000..cd9f358
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.osgi.boot;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * An OSGi configurator. See <a
+ * href="http://wiki.eclipse.org/Configurator">http:
+ * //wiki.eclipse.org/Configurator</a>
+ */
+public class Activator implements BundleActivator {
+
+       public void start(final BundleContext bundleContext) throws Exception {
+               // admin thread
+               Thread adminThread = new AdminThread(bundleContext);
+               adminThread.start();
+
+               // bootstrap
+               OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
+               osgiBoot.bootstrap();
+       }
+
+       public void stop(BundleContext context) throws Exception {
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/AdminThread.java b/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/AdminThread.java
new file mode 100644 (file)
index 0000000..6f01b89
--- /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.osgi.boot;
+
+import java.io.File;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.launch.Framework;
+
+/** Monitors the runtime and can shut it down. */
+public class AdminThread extends Thread {
+       public final static String PROP_ARGEO_OSGI_SHUTDOWN_FILE = "argeo.osgi.shutdownFile";
+       private File shutdownFile;
+       private final BundleContext bundleContext;
+
+       public AdminThread(BundleContext bundleContext) {
+               super("OSGi Boot Admin");
+               this.bundleContext = bundleContext;
+               if (System.getProperty(PROP_ARGEO_OSGI_SHUTDOWN_FILE) != null) {
+                       shutdownFile = new File(
+                                       System.getProperty(PROP_ARGEO_OSGI_SHUTDOWN_FILE));
+                       if (!shutdownFile.exists()) {
+                               shutdownFile = null;
+                               OsgiBootUtils.warn("Shutdown file " + shutdownFile
+                                               + " not found, feature deactivated");
+                       }
+               }
+       }
+
+       public void run() {
+               if (shutdownFile != null) {
+                       // wait for file to be removed
+                       while (shutdownFile.exists()) {
+                               try {
+                                       Thread.sleep(1000);
+                               } catch (InterruptedException e) {
+                                       e.printStackTrace();
+                               }
+                       }
+
+                       Framework framework = (Framework) bundleContext.getBundle(0);
+                       try {
+                               // shutdown framework
+                               framework.stop();
+                               // wait 10 mins for shutdown
+                               framework.waitForStop(10 * 60 * 1000);
+                               // close VM
+                               System.exit(0);
+                       } catch (Exception e) {
+                               e.printStackTrace();
+                               System.exit(1);
+                       }
+               }
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/DistributionBundle.java b/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/DistributionBundle.java
new file mode 100644 (file)
index 0000000..e6c0428
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * 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.osgi.boot;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.osgi.framework.Constants;
+
+/**
+ * A distribution bundle is a bundle within a maven-like distribution
+ * groupId:Bundle-SymbolicName:Bundle-Version which references others OSGi
+ * bundle. It is not required to be OSGi complete also it will generally be
+ * expected that it is. The root of the repository is computed based on the file
+ * name of the URL and of the content of the index.
+ */
+public class DistributionBundle {
+       private final static String INDEX_FILE_NAME = "modularDistribution.csv";
+
+       private final String url;
+
+       private Manifest manifest;
+       private String symbolicName;
+       private String version;
+
+       /** can be null */
+       private String baseUrl;
+       /** can be null */
+       private String relativeUrl;
+
+       private List/* <OsgiArtifact> */artifacts;
+
+       private String separator = ",";
+
+       public DistributionBundle(String url) {
+               this.url = url;
+       }
+
+       public DistributionBundle(String baseUrl, String relativeUrl) {
+               if (baseUrl == null || !baseUrl.endsWith("/"))
+                       throw new OsgiBootException("Base url " + baseUrl
+                                       + " badly formatted");
+               if (relativeUrl.startsWith("http") || relativeUrl.startsWith("file:"))
+                       throw new OsgiBootException("Relative URL " + relativeUrl
+                                       + " badly formatted");
+               this.url = baseUrl + relativeUrl;
+               this.baseUrl = baseUrl;
+               this.relativeUrl = relativeUrl;
+       }
+
+       public void processUrl() {
+               JarInputStream jarIn = null;
+               try {
+                       URL u = new URL(url);
+                       jarIn = new JarInputStream(u.openStream());
+
+                       // meta data
+                       manifest = jarIn.getManifest();
+                       symbolicName = manifest.getMainAttributes().getValue(
+                                       Constants.BUNDLE_SYMBOLICNAME);
+                       version = manifest.getMainAttributes().getValue(
+                                       Constants.BUNDLE_VERSION);
+
+                       JarEntry indexEntry;
+                       while ((indexEntry = jarIn.getNextJarEntry()) != null) {
+                               String entryName = indexEntry.getName();
+                               if (entryName.equals(INDEX_FILE_NAME)) {
+                                       break;
+                               }
+                               jarIn.closeEntry();
+                       }
+
+                       // list artifacts
+                       if (indexEntry == null)
+                               throw new OsgiBootException("No index " + INDEX_FILE_NAME
+                                               + " in " + url);
+                       artifacts = listArtifacts(jarIn);
+                       jarIn.closeEntry();
+
+                       // find base URL
+                       // won't work if distribution artifact is not listed
+                       for (int i = 0; i < artifacts.size(); i++) {
+                               OsgiArtifact osgiArtifact = (OsgiArtifact) artifacts.get(i);
+                               if (osgiArtifact.getSymbolicName().equals(symbolicName)
+                                               && osgiArtifact.getVersion().equals(version)) {
+                                       String relativeUrl = osgiArtifact.getRelativeUrl();
+                                       if (url.endsWith(relativeUrl)) {
+                                               baseUrl = url.substring(0, url.length()
+                                                               - osgiArtifact.getRelativeUrl().length());
+                                               break;
+                                       }
+                               }
+                       }
+               } catch (Exception e) {
+                       throw new OsgiBootException("Cannot list URLs from " + url, e);
+               } finally {
+                       if (jarIn != null)
+                               try {
+                                       jarIn.close();
+                               } catch (IOException e) {
+                                       // silent
+                               }
+               }
+       }
+
+       protected List/* <OsgiArtifact> */listArtifacts(InputStream in) {
+               List osgiArtifacts = new ArrayList();
+               BufferedReader reader = null;
+               try {
+                       reader = new BufferedReader(new InputStreamReader(in));
+                       String line = null;
+                       while ((line = reader.readLine()) != null) {
+                               StringTokenizer st = new StringTokenizer(line, separator);
+                               String moduleName = st.nextToken();
+                               String moduleVersion = st.nextToken();
+                               String relativeUrl = st.nextToken();
+                               osgiArtifacts.add(new OsgiArtifact(moduleName, moduleVersion,
+                                               relativeUrl));
+                       }
+               } catch (Exception e) {
+                       throw new OsgiBootException("Cannot list artifacts", e);
+               }
+               return osgiArtifacts;
+       }
+
+       /** Convenience method */
+       public static DistributionBundle processUrl(String baseUrl,
+                       String realtiveUrl) {
+               DistributionBundle distributionBundle = new DistributionBundle(baseUrl,
+                               realtiveUrl);
+               distributionBundle.processUrl();
+               return distributionBundle;
+       }
+
+       /**
+        * List full URLs of the bunmdles, based on base URL, usable directly for
+        * download.
+        */
+       public List/* <String> */listUrls() {
+               if (baseUrl == null)
+                       throw new OsgiBootException("Base URL is not set");
+
+               if (artifacts == null)
+                       throw new OsgiBootException("Artifact list not initialized");
+
+               List/* <String> */urls = new ArrayList();
+               for (int i = 0; i < artifacts.size(); i++) {
+                       OsgiArtifact osgiArtifact = (OsgiArtifact) artifacts.get(i);
+                       urls.add(baseUrl + osgiArtifact.getRelativeUrl());
+               }
+               return urls;
+       }
+
+       public void setBaseUrl(String baseUrl) {
+               this.baseUrl = baseUrl;
+       }
+
+       /** Separator used to parse the tabular file */
+       public void setSeparator(String modulesUrlSeparator) {
+               this.separator = modulesUrlSeparator;
+       }
+
+       public String getRelativeUrl() {
+               return relativeUrl;
+       }
+
+       /** One of the listed artifact */
+       protected static class OsgiArtifact {
+               private final String symbolicName;
+               private final String version;
+               private final String relativeUrl;
+
+               public OsgiArtifact(String symbolicName, String version,
+                               String relativeUrl) {
+                       super();
+                       this.symbolicName = symbolicName;
+                       this.version = version;
+                       this.relativeUrl = relativeUrl;
+               }
+
+               public String getSymbolicName() {
+                       return symbolicName;
+               }
+
+               public String getVersion() {
+                       return version;
+               }
+
+               public String getRelativeUrl() {
+                       return relativeUrl;
+               }
+
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/Launcher.java b/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/Launcher.java
new file mode 100644 (file)
index 0000000..6af1a71
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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.osgi.boot;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Vector;
+
+import org.eclipse.core.runtime.adaptor.EclipseStarter;
+import org.osgi.framework.BundleContext;
+
+/** Command line interface. */
+public class Launcher {
+
+       public static void main(String[] args) {
+               // Try to load system properties
+               String systemPropertiesFilePath = OsgiBootUtils
+                               .getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE);
+               if (systemPropertiesFilePath != null) {
+                       FileInputStream in;
+                       try {
+                               in = new FileInputStream(systemPropertiesFilePath);
+                               System.getProperties().load(in);
+                       } catch (IOException e1) {
+                               throw new RuntimeException(
+                                               "Cannot load system properties from "
+                                                               + systemPropertiesFilePath, e1);
+                       }
+                       if (in != null) {
+                               try {
+                                       in.close();
+                               } catch (Exception e) {
+                                       // silent
+                               }
+                       }
+               }
+
+               // Start main class
+               startMainClass();
+
+               // Start Equinox
+               BundleContext bundleContext = null;
+               try {
+                       bundleContext = EclipseStarter.startup(args, null);
+               } catch (Exception e) {
+                       throw new RuntimeException("Cannot start Equinox.", e);
+               }
+
+               // OSGi bootstrap
+               OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
+               osgiBoot.bootstrap();
+       }
+
+       protected static void startMainClass() {
+               String className = OsgiBootUtils
+                               .getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPCLASS);
+               if (className == null)
+                       return;
+
+               String line = System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPARGS,
+                               "");
+
+               String[] uiArgs = readArgumentsFromLine(line);
+
+               try {
+                       // Launch main method using reflection
+                       Class clss = Class.forName(className);
+                       Class[] mainArgsClasses = new Class[] { uiArgs.getClass() };
+                       Object[] mainArgs = { uiArgs };
+                       Method mainMethod = clss.getMethod("main", mainArgsClasses);
+                       mainMethod.invoke(null, mainArgs);
+               } catch (Exception e) {
+                       throw new RuntimeException("Cannot start main class.", e);
+               }
+
+       }
+
+       /**
+        * Transform a line into an array of arguments, taking "" as single
+        * arguments. (nested \" are not supported)
+        */
+       private static String[] readArgumentsFromLine(String lineOrig) {
+               String line = lineOrig.trim();// remove trailing spaces
+               List args = new Vector();
+               StringBuffer curr = new StringBuffer("");
+               boolean inQuote = false;
+               char[] arr = line.toCharArray();
+               for (int i = 0; i < arr.length; i++) {
+                       char c = arr[i];
+                       switch (c) {
+                       case '\"':
+                               inQuote = !inQuote;
+                               break;
+                       case ' ':
+                               if (!inQuote) {// otherwise, no break: goes to default
+                                       if (curr.length() > 0) {
+                                               args.add(curr.toString());
+                                               curr = new StringBuffer("");
+                                       }
+                                       break;
+                               }
+                       default:
+                               curr.append(c);
+                               break;
+                       }
+               }
+
+               // Add last arg
+               if (curr.length() > 0) {
+                       args.add(curr.toString());
+                       curr = null;
+               }
+
+               String[] res = new String[args.size()];
+               for (int i = 0; i < args.size(); i++) {
+                       res[i] = args.get(i).toString();
+               }
+               return res;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/OsgiBoot.java b/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/OsgiBoot.java
new file mode 100644 (file)
index 0000000..300ebb8
--- /dev/null
@@ -0,0 +1,1036 @@
+/*
+ * 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.osgi.boot;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.argeo.osgi.boot.internal.springutil.AntPathMatcher;
+import org.argeo.osgi.boot.internal.springutil.PathMatcher;
+import org.argeo.osgi.boot.internal.springutil.SystemPropertyUtils;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * Basic provisioning of an OSGi runtime via file path patterns and system
+ * properties. Java 1.4 compatible.<br>
+ * The approach is to generate list of URLs based on various methods, configured
+ * via system properties.
+ */
+public class OsgiBoot {
+       public final static String SYMBOLIC_NAME_OSGI_BOOT = "org.argeo.osgi.boot";
+       public final static String SYMBOLIC_NAME_EQUINOX = "org.eclipse.osgi";
+
+       public final static String PROP_OSGI_STARTLEVEL = "osgi.startLevel";
+       public final static String PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL = "osgi.bundles.defaultStartLevel";
+
+       public final static String PROP_ARGEO_OSGI_DATA_DIR = "argeo.osgi.data.dir";
+
+       public final static String PROP_ARGEO_OSGI_START = "argeo.osgi.start";
+       public final static String PROP_ARGEO_OSGI_BUNDLES = "argeo.osgi.bundles";
+       public final static String PROP_ARGEO_OSGI_LOCATIONS = "argeo.osgi.locations";
+       public final static String PROP_ARGEO_OSGI_BASE_URL = "argeo.osgi.baseUrl";
+       /** @deprecated */
+       public final static String PROP_ARGEO_OSGI_MODULES_URL = "argeo.osgi.modulesUrl";
+       public final static String PROP_ARGEO_OSGI_DISTRIBUTION_URL = "argeo.osgi.distributionUrl";
+
+       // booleans
+       public final static String PROP_ARGEO_OSGI_BOOT_DEBUG = "argeo.osgi.boot.debug";
+       public final static String PROP_ARGEO_OSGI_BOOT_EXCLUDE_SVN = "argeo.osgi.boot.excludeSvn";
+       public final static String PROP_ARGEO_OSGI_BOOT_INSTALL_IN_LEXICOGRAPHIC_ORDER = "argeo.osgi.boot.installInLexicographicOrder";
+
+       public final static String PROP_ARGEO_OSGI_BOOT_DEFAULT_TIMEOUT = "argeo.osgi.boot.defaultTimeout";
+       public final static String PROP_ARGEO_OSGI_BOOT_MODULES_URL_SEPARATOR = "argeo.osgi.boot.modulesUrlSeparator";
+       public final static String PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE = "argeo.osgi.boot.systemPropertiesFile";
+       public final static String PROP_ARGEO_OSGI_BOOT_APPCLASS = "argeo.osgi.boot.appclass";
+       public final static String PROP_ARGEO_OSGI_BOOT_APPARGS = "argeo.osgi.boot.appargs";
+
+       public final static String DEFAULT_BASE_URL = "reference:file:";
+       public final static String EXCLUDES_SVN_PATTERN = "**/.svn/**";
+
+       // OSGi system properties
+       public final static String INSTANCE_AREA_PROP = "osgi.instance.area";
+       public final static String INSTANCE_AREA_DEFAULT_PROP = "osgi.instance.area.default";
+
+       private boolean debug = Boolean.valueOf(
+                       System.getProperty(PROP_ARGEO_OSGI_BOOT_DEBUG, "false"))
+                       .booleanValue();
+       /** Exclude svn metadata implicitely(a bit costly) */
+       private boolean excludeSvn = Boolean.valueOf(
+                       System.getProperty(PROP_ARGEO_OSGI_BOOT_EXCLUDE_SVN, "false"))
+                       .booleanValue();
+
+       /**
+        * The {@link #installUrls(List)} methods won't follow the list order but
+        * order the urls according to the alphabetical order of the file names
+        * (last part of the URL). The goal is to stay closer from Eclipse PDE way
+        * of installing target platform bundles.
+        */
+       private boolean installInLexicographicOrder = Boolean
+                       .valueOf(
+                                       System.getProperty(
+                                                       PROP_ARGEO_OSGI_BOOT_INSTALL_IN_LEXICOGRAPHIC_ORDER,
+                                                       "true")).booleanValue();;
+
+       /** Default is 10s (set in constructor) */
+       private long defaultTimeout;
+
+       /** Default is ',' (set in constructor) */
+       private String modulesUrlSeparator = ",";
+
+       private final BundleContext bundleContext;
+
+       /*
+        * INITIALIZATION
+        */
+       /** Constructor */
+       public OsgiBoot(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+               defaultTimeout = Long.parseLong(OsgiBootUtils.getProperty(
+                               PROP_ARGEO_OSGI_BOOT_DEFAULT_TIMEOUT, "10000"));
+               modulesUrlSeparator = OsgiBootUtils.getProperty(
+                               PROP_ARGEO_OSGI_BOOT_MODULES_URL_SEPARATOR, ",");
+               initSystemProperties();
+       }
+
+       /**
+        * Set additional system properties, especially ${argeo.osgi.data.dir} as an
+        * OS file path (and not a file:// URL)
+        */
+       protected void initSystemProperties() {
+               String osgiInstanceArea = System.getProperty(INSTANCE_AREA_PROP);
+               String osgiInstanceAreaDefault = System
+                               .getProperty(INSTANCE_AREA_DEFAULT_PROP);
+               String tempDir = System.getProperty("java.io.tmpdir");
+
+               File dataDir = null;
+               if (osgiInstanceArea != null) {
+                       // within OSGi with -data specified
+                       osgiInstanceArea = removeFilePrefix(osgiInstanceArea);
+                       dataDir = new File(osgiInstanceArea);
+               } else if (osgiInstanceAreaDefault != null) {
+                       // within OSGi without -data specified
+                       osgiInstanceAreaDefault = removeFilePrefix(osgiInstanceAreaDefault);
+                       dataDir = new File(osgiInstanceAreaDefault);
+               } else {// outside OSGi
+                       dataDir = new File(tempDir + File.separator + "argeoOsgiData");
+               }
+               System.setProperty(PROP_ARGEO_OSGI_DATA_DIR, dataDir.getAbsolutePath());
+       }
+
+       /*
+        * HIGH-LEVEL METHODS
+        */
+       /** Bootstraps the OSGi runtime */
+       public void bootstrap() {
+               long begin = System.currentTimeMillis();
+               System.out.println();
+               OsgiBootUtils.info("OSGi bootstrap starting...");
+               OsgiBootUtils.info("Writable data directory : "
+                               + System.getProperty(PROP_ARGEO_OSGI_DATA_DIR)
+                               + " (set as system property " + PROP_ARGEO_OSGI_DATA_DIR + ")");
+               installUrls(getBundlesUrls());
+               installUrls(getLocationsUrls());
+               installUrls(getModulesUrls());
+               installUrls(getDistributionUrls());
+               checkUnresolved();
+               startBundles();
+               long duration = System.currentTimeMillis() - begin;
+               OsgiBootUtils.info("OSGi bootstrap completed in "
+                               + Math.round(((double) duration) / 1000) + "s (" + duration
+                               + "ms), " + bundleContext.getBundles().length + " bundles");
+
+               // display packages exported twice
+               if (debug) {
+                       Map /* <String,Set<String>> */duplicatePackages = findPackagesExportedTwice();
+                       if (duplicatePackages.size() > 0) {
+                               OsgiBootUtils.info("Packages exported twice:");
+                               Iterator it = duplicatePackages.keySet().iterator();
+                               while (it.hasNext()) {
+                                       String pkgName = it.next().toString();
+                                       OsgiBootUtils.info(pkgName);
+                                       Set bdles = (Set) duplicatePackages.get(pkgName);
+                                       Iterator bdlesIt = bdles.iterator();
+                                       while (bdlesIt.hasNext())
+                                               OsgiBootUtils.info("  " + bdlesIt.next());
+                               }
+                       }
+               }
+
+               System.out.println();
+       }
+
+       /*
+        * INSTALLATION
+        */
+       /** Install a single url. Convenience method. */
+       public Bundle installUrl(String url) {
+               List urls = new ArrayList();
+               urls.add(url);
+               installUrls(urls);
+               return (Bundle) getBundlesByLocation().get(url);
+       }
+
+       /** Install the bundles at this URL list. */
+       public void installUrls(List urls) {
+               Map installedBundles = getBundlesByLocation();
+
+               if (installInLexicographicOrder) {
+                       SortedMap map = new TreeMap();
+                       // reorder
+                       for (int i = 0; i < urls.size(); i++) {
+                               String url = (String) urls.get(i);
+                               int index = url.lastIndexOf('/');
+                               String fileName;
+                               if (index >= 0)
+                                       fileName = url.substring(index + 1);
+                               else
+                                       fileName = url;
+                               map.put(fileName, url);
+                       }
+
+                       // install
+                       Iterator keys = map.keySet().iterator();
+                       while (keys.hasNext()) {
+                               Object key = keys.next();
+                               String url = map.get(key).toString();
+                               installUrl(url, installedBundles);
+                       }
+               } else {
+                       for (int i = 0; i < urls.size(); i++) {
+                               String url = (String) urls.get(i);
+                               installUrl(url, installedBundles);
+                       }
+               }
+
+       }
+
+       /** Actually install the provided URL */
+       protected void installUrl(String url, Map installedBundles) {
+               try {
+                       if (installedBundles.containsKey(url)) {
+                               Bundle bundle = (Bundle) installedBundles.get(url);
+                               // bundle.update();
+                               if (debug)
+                                       debug("Bundle " + bundle.getSymbolicName()
+                                                       + " already installed from " + url);
+                       } else {
+                               Bundle bundle = bundleContext.installBundle(url);
+                               if (debug)
+                                       debug("Installed bundle " + bundle.getSymbolicName()
+                                                       + " from " + url);
+                       }
+               } catch (BundleException e) {
+                       String message = e.getMessage();
+                       if ((message.contains("Bundle \"" + SYMBOLIC_NAME_OSGI_BOOT + "\"") || message
+                                       .contains("Bundle \"" + SYMBOLIC_NAME_EQUINOX + "\""))
+                                       && message.contains("has already been installed")) {
+                               // silent, in order to avoid warnings: we know that both
+                               // have already been installed...
+                       } else {
+                               OsgiBootUtils.warn("Could not install bundle from " + url
+                                               + ": " + message);
+                       }
+                       if (debug)
+                               e.printStackTrace();
+               }
+       }
+
+       /*
+        * START
+        */
+       public void startBundles() {
+               // default and active start levels from System properties
+               Integer defaultStartLevel = new Integer(Integer.parseInt(OsgiBootUtils
+                               .getProperty(PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "4")));
+               Integer activeStartLevel = new Integer(OsgiBootUtils.getProperty(
+                               PROP_OSGI_STARTLEVEL, "6"));
+
+               SortedMap/* <Integer, List<String>> */startLevels = new TreeMap();
+               computeStartLevels(startLevels, System.getProperties(),
+                               defaultStartLevel);
+
+               Iterator/* <Integer> */levels = startLevels.keySet().iterator();
+               while (levels.hasNext()) {
+                       Integer level = (Integer) levels.next();
+                       boolean allStarted = startBundles((List) startLevels.get(level));
+                       if (!allStarted)
+                               OsgiBootUtils
+                                               .warn("Not all bundles started for level " + level);
+                       if (level.equals(activeStartLevel))
+                               break;// active start level reached
+               }
+
+       }
+
+       public static void computeStartLevels(
+                       SortedMap/* <Integer, List<String>> */startLevels,
+                       Properties properties, Integer defaultStartLevel) {
+
+               // default (and previously, only behaviour)
+               appendToStartLevels(startLevels, defaultStartLevel,
+                               properties.getProperty(PROP_ARGEO_OSGI_START, ""));
+
+               // list argeo.osgi.start.* system properties
+               Iterator/* <String> */keys = properties.keySet().iterator();
+               final String prefix = PROP_ARGEO_OSGI_START + ".";
+               while (keys.hasNext()) {
+                       String key = (String) keys.next();
+                       if (key.startsWith(prefix)) {
+                               Integer startLevel;
+                               String suffix = key.substring(prefix.length());
+                               String[] tokens = suffix.split("\\.");
+                               if (tokens.length > 0 && !tokens[0].trim().equals(""))
+                                       try {
+                                               // first token is start level
+                                               startLevel = new Integer(tokens[0]);
+                                       } catch (NumberFormatException e) {
+                                               startLevel = defaultStartLevel;
+                                       }
+                               else
+                                       startLevel = defaultStartLevel;
+
+                               // append bundle names
+                               String bundleNames = properties.getProperty(key);
+                               appendToStartLevels(startLevels, startLevel, bundleNames);
+                       }
+               }
+       }
+
+       /** Append a comma-separated list of bundles to the start levels. */
+       private static void appendToStartLevels(
+                       SortedMap/* <Integer, List<String>> */startLevels,
+                       Integer startLevel, String str) {
+               if (str == null || str.trim().equals(""))
+                       return;
+
+               if (!startLevels.containsKey(startLevel))
+                       startLevels.put(startLevel, new ArrayList());
+               String[] bundleNames = str.split(",");
+               for (int i = 0; i < bundleNames.length; i++) {
+                       if (bundleNames[i] != null && !bundleNames[i].trim().equals(""))
+                               ((List) startLevels.get(startLevel)).add(bundleNames[i]);
+               }
+       }
+
+       /**
+        * Convenience method accepting a comma-separated list of bundle to start
+        * 
+        * @deprecated
+        */
+       public void startBundles(String bundlesToStartStr) {
+               if (bundlesToStartStr == null)
+                       return;
+
+               StringTokenizer st = new StringTokenizer(bundlesToStartStr, ",");
+               List bundlesToStart = new ArrayList();
+               while (st.hasMoreTokens()) {
+                       String name = st.nextToken().trim();
+                       bundlesToStart.add(name);
+               }
+               startBundles(bundlesToStart);
+       }
+
+       /**
+        * Start the provided list of bundles
+        * 
+        * @return whether all bundlesa are now in active state
+        */
+       public boolean startBundles(List bundlesToStart) {
+               if (bundlesToStart.size() == 0)
+                       return true;
+
+               // used to monitor ACTIVE states
+               List/* <Bundle> */startedBundles = new ArrayList();
+               // used to log the bundles not found
+               List/* <String> */notFoundBundles = new ArrayList(bundlesToStart);
+
+               Bundle[] bundles = bundleContext.getBundles();
+               long startBegin = System.currentTimeMillis();
+               for (int i = 0; i < bundles.length; i++) {
+                       Bundle bundle = bundles[i];
+                       String symbolicName = bundle.getSymbolicName();
+                       if (bundlesToStart.contains(symbolicName))
+                               try {
+                                       try {
+                                               bundle.start();
+                                               if (debug)
+                                                       debug("Bundle " + symbolicName + " started");
+                                       } catch (Exception e) {
+                                               OsgiBootUtils.warn("Start of bundle " + symbolicName
+                                                               + " failed because of " + e
+                                                               + ", maybe bundle is not yet resolved,"
+                                                               + " waiting and trying again.");
+                                               waitForBundleResolvedOrActive(startBegin, bundle);
+                                               bundle.start();
+                                               startedBundles.add(bundle);
+                                       }
+                                       notFoundBundles.remove(symbolicName);
+                               } catch (Exception e) {
+                                       OsgiBootUtils.warn("Bundle " + symbolicName
+                                                       + " cannot be started: " + e.getMessage());
+                                       if (debug)
+                                               e.printStackTrace();
+                                       // was found even if start failed
+                                       notFoundBundles.remove(symbolicName);
+                               }
+               }
+
+               for (int i = 0; i < notFoundBundles.size(); i++)
+                       OsgiBootUtils.warn("Bundle '" + notFoundBundles.get(i)
+                                       + "' not started because it was not found.");
+
+               // monitors that all bundles are started
+               long beginMonitor = System.currentTimeMillis();
+               boolean allStarted = !(startedBundles.size() > 0);
+               List/* <String> */notStarted = new ArrayList();
+               while (!allStarted
+                               && (System.currentTimeMillis() - beginMonitor) < defaultTimeout) {
+                       notStarted = new ArrayList();
+                       allStarted = true;
+                       for (int i = 0; i < startedBundles.size(); i++) {
+                               Bundle bundle = (Bundle) startedBundles.get(i);
+                               // TODO check behaviour of lazs bundles
+                               if (bundle.getState() != Bundle.ACTIVE) {
+                                       allStarted = false;
+                                       notStarted.add(bundle.getSymbolicName());
+                               }
+                       }
+                       try {
+                               Thread.sleep(100);
+                       } catch (InterruptedException e) {
+                               // silent
+                       }
+               }
+               long duration = System.currentTimeMillis() - beginMonitor;
+
+               if (!allStarted)
+                       for (int i = 0; i < notStarted.size(); i++)
+                               OsgiBootUtils.warn("Bundle '" + notStarted.get(i)
+                                               + "' not ACTIVE after " + (duration / 1000) + "s");
+
+               return allStarted;
+       }
+
+       /*
+        * DIAGNOSTICS
+        */
+       /** Check unresolved bundles */
+       protected void checkUnresolved() {
+               // Refresh
+               ServiceReference packageAdminRef = bundleContext
+                               .getServiceReference(PackageAdmin.class.getName());
+               PackageAdmin packageAdmin = (PackageAdmin) bundleContext
+                               .getService(packageAdminRef);
+               packageAdmin.resolveBundles(null);
+
+               Bundle[] bundles = bundleContext.getBundles();
+               List /* Bundle */unresolvedBundles = new ArrayList();
+               for (int i = 0; i < bundles.length; i++) {
+                       int bundleState = bundles[i].getState();
+                       if (!(bundleState == Bundle.ACTIVE
+                                       || bundleState == Bundle.RESOLVED || bundleState == Bundle.STARTING))
+                               unresolvedBundles.add(bundles[i]);
+               }
+
+               if (unresolvedBundles.size() != 0) {
+                       OsgiBootUtils.warn("Unresolved bundles " + unresolvedBundles);
+               }
+       }
+
+       /** List packages exported twice. */
+       public Map findPackagesExportedTwice() {
+               ServiceReference paSr = bundleContext
+                               .getServiceReference(PackageAdmin.class.getName());
+               PackageAdmin packageAdmin = (PackageAdmin) bundleContext
+                               .getService(paSr);
+
+               // find packages exported twice
+               Bundle[] bundles = bundleContext.getBundles();
+               Map /* <String,Set<String>> */exportedPackages = new TreeMap();
+               for (int i = 0; i < bundles.length; i++) {
+                       Bundle bundle = bundles[i];
+                       ExportedPackage[] pkgs = packageAdmin.getExportedPackages(bundle);
+                       if (pkgs != null)
+                               for (int j = 0; j < pkgs.length; j++) {
+                                       String pkgName = pkgs[j].getName();
+                                       if (!exportedPackages.containsKey(pkgName)) {
+                                               exportedPackages.put(pkgName, new TreeSet());
+                                       }
+                                       ((Set) exportedPackages.get(pkgName)).add(bundle
+                                                       .getSymbolicName() + "_" + bundle.getVersion());
+                               }
+               }
+               Map /* <String,Set<String>> */duplicatePackages = new TreeMap();
+               Iterator it = exportedPackages.keySet().iterator();
+               while (it.hasNext()) {
+                       String pkgName = it.next().toString();
+                       Set bdles = (Set) exportedPackages.get(pkgName);
+                       if (bdles.size() > 1)
+                               duplicatePackages.put(pkgName, bdles);
+               }
+               return duplicatePackages;
+       }
+
+       /** Waits for a bundle to become active or resolved */
+       protected void waitForBundleResolvedOrActive(long startBegin, Bundle bundle)
+                       throws Exception {
+               int originalState = bundle.getState();
+               if ((originalState == Bundle.RESOLVED)
+                               || (originalState == Bundle.ACTIVE))
+                       return;
+
+               String originalStateStr = OsgiBootUtils.stateAsString(originalState);
+
+               int currentState = bundle.getState();
+               while (!(currentState == Bundle.RESOLVED || currentState == Bundle.ACTIVE)) {
+                       long now = System.currentTimeMillis();
+                       if ((now - startBegin) > defaultTimeout * 10)
+                               throw new Exception("Bundle " + bundle.getSymbolicName()
+                                               + " was not RESOLVED or ACTIVE after "
+                                               + (now - startBegin) + "ms (originalState="
+                                               + originalStateStr + ", currentState="
+                                               + OsgiBootUtils.stateAsString(currentState) + ")");
+
+                       try {
+                               Thread.sleep(100l);
+                       } catch (InterruptedException e) {
+                               // silent
+                       }
+                       currentState = bundle.getState();
+               }
+       }
+
+       /*
+        * EXPLICIT LOCATIONS INSTALLATION
+        */
+       /** Gets the list of resolved explicit URL locations. */
+       public List getLocationsUrls() {
+               String baseUrl = OsgiBootUtils.getProperty(PROP_ARGEO_OSGI_BASE_URL,
+                               DEFAULT_BASE_URL);
+               String bundleLocations = OsgiBootUtils
+                               .getProperty(PROP_ARGEO_OSGI_LOCATIONS);
+               return getLocationsUrls(baseUrl, bundleLocations);
+       }
+
+       /**
+        * Gets a list of URLs based on explicit locations, resolving placeholder
+        * ${...} containing system properties, e.g. ${user.home}.
+        */
+       public List getLocationsUrls(String baseUrl, String bundleLocations) {
+               List urls = new ArrayList();
+
+               if (bundleLocations == null)
+                       return urls;
+               bundleLocations = SystemPropertyUtils
+                               .resolvePlaceholders(bundleLocations);
+               if (debug)
+                       debug(PROP_ARGEO_OSGI_LOCATIONS + "=" + bundleLocations);
+
+               StringTokenizer st = new StringTokenizer(bundleLocations,
+                               File.pathSeparator);
+               while (st.hasMoreTokens()) {
+                       urls.add(locationToUrl(baseUrl, st.nextToken().trim()));
+               }
+               return urls;
+       }
+
+       /*
+        * BUNDLE PATTERNS INSTALLATION
+        */
+       /**
+        * Computes a list of URLs based on Ant-like incluide/exclude patterns
+        * defined by ${argeo.osgi.bundles} with the following format:<br>
+        * <code>/base/directory;in=*.jar;in=**;ex=org.eclipse.osgi_*;jar</code><br>
+        * WARNING: <code>/base/directory;in=*.jar,\</code> at the end of a file,
+        * without a new line causes a '.' to be appended with unexpected side
+        * effects.
+        */
+       public List getBundlesUrls() {
+               String bundlePatterns = OsgiBootUtils
+                               .getProperty(PROP_ARGEO_OSGI_BUNDLES);
+               return getBundlesUrls(bundlePatterns);
+       }
+
+       /**
+        * Compute alist of URLs to install based on the provided patterns, with
+        * default base url
+        */
+       public List getBundlesUrls(String bundlePatterns) {
+               String baseUrl = OsgiBootUtils.getProperty(PROP_ARGEO_OSGI_BASE_URL,
+                               DEFAULT_BASE_URL);
+               return getBundlesUrls(baseUrl, bundlePatterns);
+       }
+
+       /** Implements the path matching logic */
+       List getBundlesUrls(String baseUrl, String bundlePatterns) {
+               List urls = new ArrayList();
+               if (bundlePatterns == null)
+                       return urls;
+
+               bundlePatterns = SystemPropertyUtils
+                               .resolvePlaceholders(bundlePatterns);
+               if (debug)
+                       debug(PROP_ARGEO_OSGI_BUNDLES + "=" + bundlePatterns
+                                       + " (excludeSvn=" + excludeSvn + ")");
+
+               StringTokenizer st = new StringTokenizer(bundlePatterns, ",");
+               List bundlesSets = new ArrayList();
+               while (st.hasMoreTokens()) {
+                       bundlesSets.add(new BundlesSet(st.nextToken()));
+               }
+
+               // find included
+               List included = new ArrayList();
+               PathMatcher matcher = new AntPathMatcher();
+               for (int i = 0; i < bundlesSets.size(); i++) {
+                       BundlesSet bundlesSet = (BundlesSet) bundlesSets.get(i);
+                       for (int j = 0; j < bundlesSet.getIncludes().size(); j++) {
+                               String pattern = (String) bundlesSet.getIncludes().get(j);
+                               match(matcher, included, bundlesSet.getDir(), null, pattern);
+                       }
+               }
+
+               // find excluded
+               List excluded = new ArrayList();
+               for (int i = 0; i < bundlesSets.size(); i++) {
+                       BundlesSet bundlesSet = (BundlesSet) bundlesSets.get(i);
+                       for (int j = 0; j < bundlesSet.getExcludes().size(); j++) {
+                               String pattern = (String) bundlesSet.getExcludes().get(j);
+                               match(matcher, excluded, bundlesSet.getDir(), null, pattern);
+                       }
+               }
+
+               // construct list
+               for (int i = 0; i < included.size(); i++) {
+                       String fullPath = (String) included.get(i);
+                       if (!excluded.contains(fullPath))
+                               urls.add(locationToUrl(baseUrl, fullPath));
+               }
+
+               return urls;
+       }
+
+       /*
+        * DISTRIBUTION JAR INSTALLATION
+        */
+       public List getDistributionUrls() {
+               List urls = new ArrayList();
+               String distributionUrl = OsgiBootUtils
+                               .getProperty(PROP_ARGEO_OSGI_DISTRIBUTION_URL);
+               if (distributionUrl == null)
+                       return urls;
+               String baseUrl = OsgiBootUtils.getProperty(PROP_ARGEO_OSGI_BASE_URL);
+
+               DistributionBundle distributionBundle;
+               if (baseUrl != null
+                               && !(distributionUrl.startsWith("http") || distributionUrl
+                                               .startsWith("file"))) {
+                       // relative url
+                       distributionBundle = new DistributionBundle(baseUrl,
+                                       distributionUrl);
+               } else {
+                       distributionBundle = new DistributionBundle(distributionUrl);
+                       if (baseUrl != null)
+                               distributionBundle.setBaseUrl(baseUrl);
+
+               }
+               distributionBundle.processUrl();
+               return distributionBundle.listUrls();
+       }
+
+       /*
+        * MODULES LIST INSTALLATION (${argeo.osgi.modulesUrl})
+        */
+       /**
+        * Downloads a list of URLs in CSV format from ${argeo.osgi.modulesUrl}:<br>
+        * <code>Bundle-SymbolicName,Bundle-Version,url</code>)<br>
+        * If ${argeo.osgi.baseUrl} is set, URLs will be considered relative paths
+        * and be concatenated with the base URL, typically the root of a Maven
+        * repository.
+        * 
+        * @deprecated
+        */
+       public List getModulesUrls() {
+               List urls = new ArrayList();
+               String modulesUrlStr = OsgiBootUtils
+                               .getProperty(PROP_ARGEO_OSGI_MODULES_URL);
+               if (modulesUrlStr == null)
+                       return urls;
+
+               String baseUrl = OsgiBootUtils.getProperty(PROP_ARGEO_OSGI_BASE_URL);
+
+               Map installedBundles = getBundlesBySymbolicName();
+
+               BufferedReader reader = null;
+               try {
+                       URL modulesUrl = new URL(modulesUrlStr);
+                       reader = new BufferedReader(new InputStreamReader(
+                                       modulesUrl.openStream()));
+                       String line = null;
+                       while ((line = reader.readLine()) != null) {
+                               StringTokenizer st = new StringTokenizer(line,
+                                               modulesUrlSeparator);
+                               String moduleName = st.nextToken();
+                               String moduleVersion = st.nextToken();
+                               String url = st.nextToken();
+                               if (baseUrl != null)
+                                       url = baseUrl + url;
+
+                               if (installedBundles.containsKey(moduleName)) {
+                                       Bundle bundle = (Bundle) installedBundles.get(moduleName);
+                                       String bundleVersion = bundle.getHeaders()
+                                                       .get(Constants.BUNDLE_VERSION).toString();
+                                       int comp = OsgiBootUtils.compareVersions(bundleVersion,
+                                                       moduleVersion);
+                                       if (comp > 0) {
+                                               OsgiBootUtils.warn("Installed version " + bundleVersion
+                                                               + " of bundle " + moduleName
+                                                               + " is newer than  provided version "
+                                                               + moduleVersion);
+                                       } else if (comp < 0) {
+                                               urls.add(url);
+                                               OsgiBootUtils.info("Updated bundle " + moduleName
+                                                               + " with version " + moduleVersion
+                                                               + " (old version was " + bundleVersion + ")");
+                                       } else {
+                                               // do nothing
+                                       }
+                               } else {
+                                       urls.add(url);
+                               }
+                       }
+               } catch (Exception e1) {
+                       throw new RuntimeException("Cannot read url " + modulesUrlStr, e1);
+               } finally {
+                       if (reader != null)
+                               try {
+                                       reader.close();
+                               } catch (IOException e) {
+                                       e.printStackTrace();
+                               }
+               }
+               return urls;
+       }
+
+       /*
+        * HIGH LEVEL UTILITIES
+        */
+       /** Actually performs the matching logic. */
+       protected void match(PathMatcher matcher, List matched, String base,
+                       String currentPath, String pattern) {
+               if (currentPath == null) {
+                       // Init
+                       File baseDir = new File(base.replace('/', File.separatorChar));
+                       File[] files = baseDir.listFiles();
+
+                       if (files == null) {
+                               if (debug)
+                                       OsgiBootUtils.warn("Base dir " + baseDir
+                                                       + " has no children, exists=" + baseDir.exists()
+                                                       + ", isDirectory=" + baseDir.isDirectory());
+                               return;
+                       }
+
+                       for (int i = 0; i < files.length; i++)
+                               match(matcher, matched, base, files[i].getName(), pattern);
+               } else {
+                       String fullPath = base + '/' + currentPath;
+                       if (matched.contains(fullPath))
+                               return;// don't try deeper if already matched
+
+                       boolean ok = matcher.match(pattern, currentPath);
+                       // if (debug)
+                       // debug(currentPath + " " + (ok ? "" : " not ")
+                       // + " matched with " + pattern);
+                       if (ok) {
+                               matched.add(fullPath);
+                               return;
+                       } else {
+                               String newFullPath = relativeToFullPath(base, currentPath);
+                               File newFile = new File(newFullPath);
+                               File[] files = newFile.listFiles();
+                               if (files != null) {
+                                       for (int i = 0; i < files.length; i++) {
+                                               String newCurrentPath = currentPath + '/'
+                                                               + files[i].getName();
+                                               if (files[i].isDirectory()) {
+                                                       if (matcher.matchStart(pattern, newCurrentPath)) {
+                                                               // recurse only if start matches
+                                                               match(matcher, matched, base, newCurrentPath,
+                                                                               pattern);
+                                                       } else {
+                                                               if (debug)
+                                                                       debug(newCurrentPath
+                                                                                       + " does not start match with "
+                                                                                       + pattern);
+
+                                                       }
+                                               } else {
+                                                       boolean nonDirectoryOk = matcher.match(pattern,
+                                                                       newCurrentPath);
+                                                       if (debug)
+                                                               debug(currentPath + " " + (ok ? "" : " not ")
+                                                                               + " matched with " + pattern);
+                                                       if (nonDirectoryOk)
+                                                               matched.add(relativeToFullPath(base,
+                                                                               newCurrentPath));
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       protected void matchFile() {
+
+       }
+
+       /*
+        * LOW LEVEL UTILITIES
+        */
+       /**
+        * The bundles already installed. Key is location (String) , value is a
+        * {@link Bundle}
+        */
+       public Map getBundlesByLocation() {
+               Map installedBundles = new HashMap();
+               Bundle[] bundles = bundleContext.getBundles();
+               for (int i = 0; i < bundles.length; i++) {
+                       installedBundles.put(bundles[i].getLocation(), bundles[i]);
+               }
+               return installedBundles;
+       }
+
+       /**
+        * The bundles already installed. Key is symbolic name (String) , value is a
+        * {@link Bundle}
+        */
+       public Map getBundlesBySymbolicName() {
+               Map namedBundles = new HashMap();
+               Bundle[] bundles = bundleContext.getBundles();
+               for (int i = 0; i < bundles.length; i++) {
+                       namedBundles.put(bundles[i].getSymbolicName(), bundles[i]);
+               }
+               return namedBundles;
+       }
+
+       /** Creates an URL from a location */
+       protected String locationToUrl(String baseUrl, String location) {
+               // int extInd = location.lastIndexOf('.');
+               // String ext = null;
+               // if (extInd > 0)
+               // ext = location.substring(extInd);
+               //
+               // if (baseUrl.startsWith("reference:") && ".jar".equals(ext))
+               // return "file:" + location;
+               // else
+               return baseUrl + location;
+       }
+
+       /** Transforms a relative path in a full system path. */
+       protected String relativeToFullPath(String basePath, String relativePath) {
+               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;
+       }
+
+       /**
+        * Convenience method to avoid cluttering the code with
+        * OsgiBootUtils.debug()
+        */
+       protected void debug(Object obj) {
+               OsgiBootUtils.debug(obj);
+       }
+
+       /*
+        * BEAN METHODS
+        */
+
+       public boolean getDebug() {
+               return debug;
+       }
+
+       public void setDebug(boolean debug) {
+               this.debug = debug;
+       }
+
+       public BundleContext getBundleContext() {
+               return bundleContext;
+       }
+
+       public void setInstallInLexicographicOrder(
+                       boolean installInAlphabeticalOrder) {
+               this.installInLexicographicOrder = installInAlphabeticalOrder;
+       }
+
+       public boolean isInstallInLexicographicOrder() {
+               return installInLexicographicOrder;
+       }
+
+       public void setDefaultTimeout(long defaultTimeout) {
+               this.defaultTimeout = defaultTimeout;
+       }
+
+       public void setModulesUrlSeparator(String modulesUrlSeparator) {
+               this.modulesUrlSeparator = modulesUrlSeparator;
+       }
+
+       public boolean isExcludeSvn() {
+               return excludeSvn;
+       }
+
+       public void setExcludeSvn(boolean excludeSvn) {
+               this.excludeSvn = excludeSvn;
+       }
+
+       /*
+        * INTERNAL CLASSES
+        */
+
+       /** Intermediary structure used by path matching */
+       protected class BundlesSet {
+               private String baseUrl = "reference:file";// not used yet
+               private final String dir;
+               private List includes = new ArrayList();
+               private List excludes = new ArrayList();
+
+               public BundlesSet(String def) {
+                       StringTokenizer st = new StringTokenizer(def, ";");
+
+                       if (!st.hasMoreTokens())
+                               throw new RuntimeException("Base dir not defined.");
+                       try {
+                               String dirPath = st.nextToken();
+
+                               if (dirPath.startsWith("file:"))
+                                       dirPath = dirPath.substring("file:".length());
+
+                               dir = new File(dirPath.replace('/', File.separatorChar))
+                                               .getCanonicalPath();
+                               if (debug)
+                                       debug("Base dir: " + dir);
+                       } catch (IOException e) {
+                               throw new RuntimeException("Cannot convert to absolute path", e);
+                       }
+
+                       while (st.hasMoreTokens()) {
+                               String tk = st.nextToken();
+                               StringTokenizer stEq = new StringTokenizer(tk, "=");
+                               String type = stEq.nextToken();
+                               String pattern = stEq.nextToken();
+                               if ("in".equals(type) || "include".equals(type)) {
+                                       includes.add(pattern);
+                               } else if ("ex".equals(type) || "exclude".equals(type)) {
+                                       excludes.add(pattern);
+                               } else if ("baseUrl".equals(type)) {
+                                       baseUrl = pattern;
+                               } else {
+                                       System.err.println("Unkown bundles pattern type " + type);
+                               }
+                       }
+
+                       if (excludeSvn && !excludes.contains(EXCLUDES_SVN_PATTERN)) {
+                               excludes.add(EXCLUDES_SVN_PATTERN);
+                       }
+               }
+
+               public String getDir() {
+                       return dir;
+               }
+
+               public List getIncludes() {
+                       return includes;
+               }
+
+               public List getExcludes() {
+                       return excludes;
+               }
+
+               public String getBaseUrl() {
+                       return baseUrl;
+               }
+
+       }
+
+       /* @deprecated Doesn't seem to be used anymore. */
+       // public void installOrUpdateUrls(Map urls) {
+       // Map installedBundles = getBundles();
+       //
+       // for (Iterator modules = urls.keySet().iterator(); modules.hasNext();) {
+       // String moduleName = (String) modules.next();
+       // String urlStr = (String) urls.get(moduleName);
+       // if (installedBundles.containsKey(moduleName)) {
+       // Bundle bundle = (Bundle) installedBundles.get(moduleName);
+       // InputStream in;
+       // try {
+       // URL url = new URL(urlStr);
+       // in = url.openStream();
+       // bundle.update(in);
+       // OsgiBootUtils.info("Updated bundle " + moduleName
+       // + " from " + urlStr);
+       // } catch (Exception e) {
+       // throw new RuntimeException("Cannot update " + moduleName
+       // + " from " + urlStr);
+       // }
+       // if (in != null)
+       // try {
+       // in.close();
+       // } catch (IOException e) {
+       // e.printStackTrace();
+       // }
+       // } else {
+       // try {
+       // Bundle bundle = bundleContext.installBundle(urlStr);
+       // if (debug)
+       // debug("Installed bundle " + bundle.getSymbolicName()
+       // + " from " + urlStr);
+       // } catch (BundleException e) {
+       // OsgiBootUtils.warn("Could not install bundle from "
+       // + urlStr + ": " + e.getMessage());
+       // }
+       // }
+       // }
+       //
+       // }
+
+}
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/OsgiBootException.java b/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/OsgiBootException.java
new file mode 100644 (file)
index 0000000..9a75f17
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.osgi.boot;
+
+/** OsgiBoot specific exceptions */
+public class OsgiBootException extends RuntimeException {
+       private static final long serialVersionUID = 2414011711711425353L;
+
+       public OsgiBootException() {
+       }
+
+       public OsgiBootException(String message) {
+               super(message);
+       }
+
+       public OsgiBootException(String message, Throwable cause) {
+               super(message, cause);
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/OsgiBootUtils.java b/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/OsgiBootUtils.java
new file mode 100644 (file)
index 0000000..20891c7
--- /dev/null
@@ -0,0 +1,142 @@
+/*\r
+ * Copyright (C) 2007-2012 Argeo GmbH\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *         http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.argeo.osgi.boot;\r
+\r
+import java.text.DateFormat;\r
+import java.text.SimpleDateFormat;\r
+import java.util.ArrayList;\r
+import java.util.Date;\r
+import java.util.List;\r
+import java.util.StringTokenizer;\r
+\r
+import org.osgi.framework.Bundle;\r
+\r
+/** Utilities, mostly related to logging. */\r
+public class OsgiBootUtils {\r
+       /** ISO8601 (as per log4j) and difference to UTC */\r
+       private static DateFormat dateFormat = new SimpleDateFormat(\r
+                       "yyyy-MM-dd HH:mm:ss,SSS Z");\r
+\r
+       public static void info(Object obj) {\r
+               System.out.println("# OSGiBOOT      # " + dateFormat.format(new Date())\r
+                               + " # " + obj);\r
+       }\r
+\r
+       public static void debug(Object obj) {\r
+               System.out.println("# OSGiBOOT DBG  # " + dateFormat.format(new Date())\r
+                               + " # " + obj);\r
+       }\r
+\r
+       public static void warn(Object obj) {\r
+               System.out.println("# OSGiBOOT WARN # " + dateFormat.format(new Date())\r
+                               + " # " + obj);\r
+       }\r
+\r
+       /**\r
+        * Gets a property value\r
+        * \r
+        * @return null when defaultValue is ""\r
+        */\r
+       public static String getProperty(String name, String defaultValue) {\r
+               final String value;\r
+               if (defaultValue != null)\r
+                       value = System.getProperty(name, defaultValue);\r
+               else\r
+                       value = System.getProperty(name);\r
+\r
+               if (value == null || value.equals(""))\r
+                       return null;\r
+               else\r
+                       return value;\r
+       }\r
+\r
+       public static String getProperty(String name) {\r
+               return getProperty(name, null);\r
+       }\r
+\r
+       public static String stateAsString(int state) {\r
+               switch (state) {\r
+               case Bundle.UNINSTALLED:\r
+                       return "UNINSTALLED";\r
+               case Bundle.INSTALLED:\r
+                       return "INSTALLED";\r
+               case Bundle.RESOLVED:\r
+                       return "RESOLVED";\r
+               case Bundle.STARTING:\r
+                       return "STARTING";\r
+               case Bundle.ACTIVE:\r
+                       return "ACTIVE";\r
+               case Bundle.STOPPING:\r
+                       return "STOPPING";\r
+               default:\r
+                       return Integer.toString(state);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * @return ==0: versions are identical, <0: tested version is newer, >0:\r
+        *         currentVersion is newer.\r
+        */\r
+       public static int compareVersions(String currentVersion,\r
+                       String testedVersion) {\r
+               List cToks = new ArrayList();\r
+               StringTokenizer cSt = new StringTokenizer(currentVersion, ".");\r
+               while (cSt.hasMoreTokens())\r
+                       cToks.add(cSt.nextToken());\r
+               List tToks = new ArrayList();\r
+               StringTokenizer tSt = new StringTokenizer(currentVersion, ".");\r
+               while (tSt.hasMoreTokens())\r
+                       tToks.add(tSt.nextToken());\r
+       \r
+               int comp = 0;\r
+               comp: for (int i = 0; i < cToks.size(); i++) {\r
+                       if (tToks.size() <= i) {\r
+                               // equals until then, tested shorter\r
+                               comp = 1;\r
+                               break comp;\r
+                       }\r
+       \r
+                       String c = (String) cToks.get(i);\r
+                       String t = (String) tToks.get(i);\r
+       \r
+                       try {\r
+                               int cInt = Integer.parseInt(c);\r
+                               int tInt = Integer.parseInt(t);\r
+                               if (cInt == tInt)\r
+                                       continue comp;\r
+                               else {\r
+                                       comp = (cInt - tInt);\r
+                                       break comp;\r
+                               }\r
+                       } catch (NumberFormatException e) {\r
+                               if (c.equals(t))\r
+                                       continue comp;\r
+                               else {\r
+                                       comp = c.compareTo(t);\r
+                                       break comp;\r
+                               }\r
+                       }\r
+               }\r
+       \r
+               if (comp == 0 && tToks.size() > cToks.size()) {\r
+                       // equals until then, current shorter\r
+                       comp = -1;\r
+               }\r
+       \r
+               return comp;\r
+       }\r
+\r
+}\r
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/AntPathMatcher.java b/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/AntPathMatcher.java
new file mode 100644 (file)
index 0000000..0b9ad1a
--- /dev/null
@@ -0,0 +1,411 @@
+/*\r
+ * Copyright 2002-2007 the original author or authors.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.argeo.osgi.boot.internal.springutil;\r
+\r
+/**\r
+ * PathMatcher implementation for Ant-style path patterns.\r
+ * Examples are provided below.\r
+ *\r
+ * <p>Part of this mapping code has been kindly borrowed from\r
+ * <a href="http://ant.apache.org">Apache Ant</a>.\r
+ *\r
+ * <p>The mapping matches URLs using the following rules:<br>\r
+ * <ul>\r
+ * <li>? matches one character</li>\r
+ * <li>* matches zero or more characters</li>\r
+ * <li>** matches zero or more 'directories' in a path</li>\r
+ * </ul>\r
+ *\r
+ * <p>Some examples:<br>\r
+ * <ul>\r
+ * <li><code>com/t?st.jsp</code> - matches <code>com/test.jsp</code> but also\r
+ * <code>com/tast.jsp</code> or <code>com/txst.jsp</code></li>\r
+ * <li><code>com/*.jsp</code> - matches all <code>.jsp</code> files in the\r
+ * <code>com</code> directory</li>\r
+ * <li><code>com/&#42;&#42;/test.jsp</code> - matches all <code>test.jsp</code>\r
+ * files underneath the <code>com</code> path</li>\r
+ * <li><code>org/springframework/&#42;&#42;/*.jsp</code> - matches all <code>.jsp</code>\r
+ * files underneath the <code>org/springframework</code> path</li>\r
+ * <li><code>org/&#42;&#42;/servlet/bla.jsp</code> - matches\r
+ * <code>org/springframework/servlet/bla.jsp</code> but also\r
+ * <code>org/springframework/testing/servlet/bla.jsp</code> and\r
+ * <code>org/servlet/bla.jsp</code></li>\r
+ * </ul>\r
+ *\r
+ * @author Alef Arendsen\r
+ * @author Juergen Hoeller\r
+ * @author Rob Harrop\r
+ * @since 16.07.2003\r
+ */\r
+public class AntPathMatcher implements PathMatcher {\r
+\r
+       /** Default path separator: "/" */\r
+       public static final String DEFAULT_PATH_SEPARATOR = "/";\r
+\r
+       private String pathSeparator = DEFAULT_PATH_SEPARATOR;\r
+\r
+\r
+       /**\r
+        * Set the path separator to use for pattern parsing.\r
+        * Default is "/", as in Ant.\r
+        */\r
+       public void setPathSeparator(String pathSeparator) {\r
+               this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR);\r
+       }\r
+\r
+\r
+       public boolean isPattern(String path) {\r
+               return (path.indexOf('*') != -1 || path.indexOf('?') != -1);\r
+       }\r
+\r
+       public boolean match(String pattern, String path) {\r
+               return doMatch(pattern, path, true);\r
+       }\r
+\r
+       public boolean matchStart(String pattern, String path) {\r
+               return doMatch(pattern, path, false);\r
+       }\r
+\r
+\r
+       /**\r
+        * Actually match the given <code>path</code> against the given <code>pattern</code>.\r
+        * @param pattern the pattern to match against\r
+        * @param path the path String to test\r
+        * @param fullMatch whether a full pattern match is required\r
+        * (else a pattern match as far as the given base path goes is sufficient)\r
+        * @return <code>true</code> if the supplied <code>path</code> matched,\r
+        * <code>false</code> if it didn't\r
+        */\r
+       protected boolean doMatch(String pattern, String path, boolean fullMatch) {\r
+               if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {\r
+                       return false;\r
+               }\r
+\r
+               String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);\r
+               String[] pathDirs = StringUtils.tokenizeToStringArray(path, this.pathSeparator);\r
+\r
+               int pattIdxStart = 0;\r
+               int pattIdxEnd = pattDirs.length - 1;\r
+               int pathIdxStart = 0;\r
+               int pathIdxEnd = pathDirs.length - 1;\r
+\r
+               // Match all elements up to the first **\r
+               while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {\r
+                       String patDir = pattDirs[pattIdxStart];\r
+                       if ("**".equals(patDir)) {\r
+                               break;\r
+                       }\r
+                       if (!matchStrings(patDir, pathDirs[pathIdxStart])) {\r
+                               return false;\r
+                       }\r
+                       pattIdxStart++;\r
+                       pathIdxStart++;\r
+               }\r
+\r
+               if (pathIdxStart > pathIdxEnd) {\r
+                       // Path is exhausted, only match if rest of pattern is * or **'s\r
+                       if (pattIdxStart > pattIdxEnd) {\r
+                               return (pattern.endsWith(this.pathSeparator) ?\r
+                                               path.endsWith(this.pathSeparator) : !path.endsWith(this.pathSeparator));\r
+                       }\r
+                       if (!fullMatch) {\r
+                               return true;\r
+                       }\r
+                       if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") &&\r
+                                       path.endsWith(this.pathSeparator)) {\r
+                               return true;\r
+                       }\r
+                       for (int i = pattIdxStart; i <= pattIdxEnd; i++) {\r
+                               if (!pattDirs[i].equals("**")) {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       return true;\r
+               }\r
+               else if (pattIdxStart > pattIdxEnd) {\r
+                       // String not exhausted, but pattern is. Failure.\r
+                       return false;\r
+               }\r
+               else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {\r
+                       // Path start definitely matches due to "**" part in pattern.\r
+                       return true;\r
+               }\r
+\r
+               // up to last '**'\r
+               while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {\r
+                       String patDir = pattDirs[pattIdxEnd];\r
+                       if (patDir.equals("**")) {\r
+                               break;\r
+                       }\r
+                       if (!matchStrings(patDir, pathDirs[pathIdxEnd])) {\r
+                               return false;\r
+                       }\r
+                       pattIdxEnd--;\r
+                       pathIdxEnd--;\r
+               }\r
+               if (pathIdxStart > pathIdxEnd) {\r
+                       // String is exhausted\r
+                       for (int i = pattIdxStart; i <= pattIdxEnd; i++) {\r
+                               if (!pattDirs[i].equals("**")) {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       return true;\r
+               }\r
+\r
+               while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {\r
+                       int patIdxTmp = -1;\r
+                       for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {\r
+                               if (pattDirs[i].equals("**")) {\r
+                                       patIdxTmp = i;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (patIdxTmp == pattIdxStart + 1) {\r
+                               // '**/**' situation, so skip one\r
+                               pattIdxStart++;\r
+                               continue;\r
+                       }\r
+                       // Find the pattern between padIdxStart & padIdxTmp in str between\r
+                       // strIdxStart & strIdxEnd\r
+                       int patLength = (patIdxTmp - pattIdxStart - 1);\r
+                       int strLength = (pathIdxEnd - pathIdxStart + 1);\r
+                       int foundIdx = -1;\r
+\r
+                       strLoop:\r
+                           for (int i = 0; i <= strLength - patLength; i++) {\r
+                                   for (int j = 0; j < patLength; j++) {\r
+                                           String subPat = (String) pattDirs[pattIdxStart + j + 1];\r
+                                           String subStr = (String) pathDirs[pathIdxStart + i + j];\r
+                                           if (!matchStrings(subPat, subStr)) {\r
+                                                   continue strLoop;\r
+                                           }\r
+                                   }\r
+                                   foundIdx = pathIdxStart + i;\r
+                                   break;\r
+                           }\r
+\r
+                       if (foundIdx == -1) {\r
+                               return false;\r
+                       }\r
+\r
+                       pattIdxStart = patIdxTmp;\r
+                       pathIdxStart = foundIdx + patLength;\r
+               }\r
+\r
+               for (int i = pattIdxStart; i <= pattIdxEnd; i++) {\r
+                       if (!pattDirs[i].equals("**")) {\r
+                               return false;\r
+                       }\r
+               }\r
+\r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * Tests whether or not a string matches against a pattern.\r
+        * The pattern may contain two special characters:<br>\r
+        * '*' means zero or more characters<br>\r
+        * '?' means one and only one character\r
+        * @param pattern pattern to match against.\r
+        * Must not be <code>null</code>.\r
+        * @param str string which must be matched against the pattern.\r
+        * Must not be <code>null</code>.\r
+        * @return <code>true</code> if the string matches against the\r
+        * pattern, or <code>false</code> otherwise.\r
+        */\r
+       private boolean matchStrings(String pattern, String str) {\r
+               char[] patArr = pattern.toCharArray();\r
+               char[] strArr = str.toCharArray();\r
+               int patIdxStart = 0;\r
+               int patIdxEnd = patArr.length - 1;\r
+               int strIdxStart = 0;\r
+               int strIdxEnd = strArr.length - 1;\r
+               char ch;\r
+\r
+               boolean containsStar = false;\r
+               for (int i = 0; i < patArr.length; i++) {\r
+                       if (patArr[i] == '*') {\r
+                               containsStar = true;\r
+                               break;\r
+                       }\r
+               }\r
+\r
+               if (!containsStar) {\r
+                       // No '*'s, so we make a shortcut\r
+                       if (patIdxEnd != strIdxEnd) {\r
+                               return false; // Pattern and string do not have the same size\r
+                       }\r
+                       for (int i = 0; i <= patIdxEnd; i++) {\r
+                               ch = patArr[i];\r
+                               if (ch != '?') {\r
+                                       if (ch != strArr[i]) {\r
+                                               return false;// Character mismatch\r
+                                       }\r
+                               }\r
+                       }\r
+                       return true; // String matches against pattern\r
+               }\r
+\r
+\r
+               if (patIdxEnd == 0) {\r
+                       return true; // Pattern contains only '*', which matches anything\r
+               }\r
+\r
+               // Process characters before first star\r
+               while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {\r
+                       if (ch != '?') {\r
+                               if (ch != strArr[strIdxStart]) {\r
+                                       return false;// Character mismatch\r
+                               }\r
+                       }\r
+                       patIdxStart++;\r
+                       strIdxStart++;\r
+               }\r
+               if (strIdxStart > strIdxEnd) {\r
+                       // All characters in the string are used. Check if only '*'s are\r
+                       // left in the pattern. If so, we succeeded. Otherwise failure.\r
+                       for (int i = patIdxStart; i <= patIdxEnd; i++) {\r
+                               if (patArr[i] != '*') {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       return true;\r
+               }\r
+\r
+               // Process characters after last star\r
+               while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {\r
+                       if (ch != '?') {\r
+                               if (ch != strArr[strIdxEnd]) {\r
+                                       return false;// Character mismatch\r
+                               }\r
+                       }\r
+                       patIdxEnd--;\r
+                       strIdxEnd--;\r
+               }\r
+               if (strIdxStart > strIdxEnd) {\r
+                       // All characters in the string are used. Check if only '*'s are\r
+                       // left in the pattern. If so, we succeeded. Otherwise failure.\r
+                       for (int i = patIdxStart; i <= patIdxEnd; i++) {\r
+                               if (patArr[i] != '*') {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       return true;\r
+               }\r
+\r
+               // process pattern between stars. padIdxStart and patIdxEnd point\r
+               // always to a '*'.\r
+               while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {\r
+                       int patIdxTmp = -1;\r
+                       for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {\r
+                               if (patArr[i] == '*') {\r
+                                       patIdxTmp = i;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (patIdxTmp == patIdxStart + 1) {\r
+                               // Two stars next to each other, skip the first one.\r
+                               patIdxStart++;\r
+                               continue;\r
+                       }\r
+                       // Find the pattern between padIdxStart & padIdxTmp in str between\r
+                       // strIdxStart & strIdxEnd\r
+                       int patLength = (patIdxTmp - patIdxStart - 1);\r
+                       int strLength = (strIdxEnd - strIdxStart + 1);\r
+                       int foundIdx = -1;\r
+                       strLoop:\r
+                       for (int i = 0; i <= strLength - patLength; i++) {\r
+                               for (int j = 0; j < patLength; j++) {\r
+                                       ch = patArr[patIdxStart + j + 1];\r
+                                       if (ch != '?') {\r
+                                               if (ch != strArr[strIdxStart + i + j]) {\r
+                                                       continue strLoop;\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               foundIdx = strIdxStart + i;\r
+                               break;\r
+                       }\r
+\r
+                       if (foundIdx == -1) {\r
+                               return false;\r
+                       }\r
+\r
+                       patIdxStart = patIdxTmp;\r
+                       strIdxStart = foundIdx + patLength;\r
+               }\r
+\r
+               // All characters in the string are used. Check if only '*'s are left\r
+               // in the pattern. If so, we succeeded. Otherwise failure.\r
+               for (int i = patIdxStart; i <= patIdxEnd; i++) {\r
+                       if (patArr[i] != '*') {\r
+                               return false;\r
+                       }\r
+               }\r
+\r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * Given a pattern and a full path, determine the pattern-mapped part.\r
+        * <p>For example:\r
+        * <ul>\r
+        * <li>'<code>/docs/cvs/commit.html</code>' and '<code>/docs/cvs/commit.html</code> -> ''</li>\r
+        * <li>'<code>/docs/*</code>' and '<code>/docs/cvs/commit</code> -> '<code>cvs/commit</code>'</li>\r
+        * <li>'<code>/docs/cvs/*.html</code>' and '<code>/docs/cvs/commit.html</code> -> '<code>commit.html</code>'</li>\r
+        * <li>'<code>/docs/**</code>' and '<code>/docs/cvs/commit</code> -> '<code>cvs/commit</code>'</li>\r
+        * <li>'<code>/docs/**\/*.html</code>' and '<code>/docs/cvs/commit.html</code> -> '<code>cvs/commit.html</code>'</li>\r
+        * <li>'<code>/*.html</code>' and '<code>/docs/cvs/commit.html</code> -> '<code>docs/cvs/commit.html</code>'</li>\r
+        * <li>'<code>*.html</code>' and '<code>/docs/cvs/commit.html</code> -> '<code>/docs/cvs/commit.html</code>'</li>\r
+        * <li>'<code>*</code>' and '<code>/docs/cvs/commit.html</code> -> '<code>/docs/cvs/commit.html</code>'</li>\r
+        * </ul>\r
+        * <p>Assumes that {@link #match} returns <code>true</code> for '<code>pattern</code>'\r
+        * and '<code>path</code>', but does <strong>not</strong> enforce this.\r
+        */\r
+       public String extractPathWithinPattern(String pattern, String path) {\r
+               String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);\r
+               String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator);\r
+\r
+               StringBuffer buffer = new StringBuffer();\r
+\r
+               // Add any path parts that have a wildcarded pattern part.\r
+               int puts = 0;\r
+               for (int i = 0; i < patternParts.length; i++) {\r
+                       String patternPart = patternParts[i];\r
+                       if ((patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) && pathParts.length >= i + 1) {\r
+                               if (puts > 0 || (i == 0 && !pattern.startsWith(this.pathSeparator))) {\r
+                                       buffer.append(this.pathSeparator);\r
+                               }\r
+                               buffer.append(pathParts[i]);\r
+                               puts++;\r
+                       }\r
+               }\r
+\r
+               // Append any trailing path parts.\r
+               for (int i = patternParts.length; i < pathParts.length; i++) {\r
+                       if (puts > 0 || i > 0) {\r
+                               buffer.append(this.pathSeparator);\r
+                       }\r
+                       buffer.append(pathParts[i]);\r
+               }\r
+\r
+               return buffer.toString();\r
+       }\r
+\r
+}\r
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/CollectionUtils.java b/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/CollectionUtils.java
new file mode 100644 (file)
index 0000000..ad01542
--- /dev/null
@@ -0,0 +1,275 @@
+/*\r
+ * Copyright 2002-2008 the original author or authors.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.argeo.osgi.boot.internal.springutil;\r
+\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.Enumeration;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Properties;\r
+\r
+/**\r
+ * Miscellaneous collection utility methods.\r
+ * Mainly for internal use within the framework.\r
+ *\r
+ * @author Juergen Hoeller\r
+ * @author Rob Harrop\r
+ * @since 1.1.3\r
+ */\r
+public abstract class CollectionUtils {\r
+\r
+       /**\r
+        * Return <code>true</code> if the supplied Collection is <code>null</code>\r
+        * or empty. Otherwise, return <code>false</code>.\r
+        * @param collection the Collection to check\r
+        * @return whether the given Collection is empty\r
+        */\r
+       public static boolean isEmpty(Collection collection) {\r
+               return (collection == null || collection.isEmpty());\r
+       }\r
+\r
+       /**\r
+        * Return <code>true</code> if the supplied Map is <code>null</code>\r
+        * or empty. Otherwise, return <code>false</code>.\r
+        * @param map the Map to check\r
+        * @return whether the given Map is empty\r
+        */\r
+       public static boolean isEmpty(Map map) {\r
+               return (map == null || map.isEmpty());\r
+       }\r
+\r
+       /**\r
+        * Convert the supplied array into a List. A primitive array gets\r
+        * converted into a List of the appropriate wrapper type.\r
+        * <p>A <code>null</code> source value will be converted to an\r
+        * empty List.\r
+        * @param source the (potentially primitive) array\r
+        * @return the converted List result\r
+        * @see ObjectUtils#toObjectArray(Object)\r
+        */\r
+       public static List arrayToList(Object source) {\r
+               return Arrays.asList(ObjectUtils.toObjectArray(source));\r
+       }\r
+\r
+       /**\r
+        * Merge the given array into the given Collection.\r
+        * @param array the array to merge (may be <code>null</code>)\r
+        * @param collection the target Collection to merge the array into\r
+        */\r
+       public static void mergeArrayIntoCollection(Object array, Collection collection) {\r
+               if (collection == null) {\r
+                       throw new IllegalArgumentException("Collection must not be null");\r
+               }\r
+               Object[] arr = ObjectUtils.toObjectArray(array);\r
+               for (int i = 0; i < arr.length; i++) {\r
+                       collection.add(arr[i]);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Merge the given Properties instance into the given Map,\r
+        * copying all properties (key-value pairs) over.\r
+        * <p>Uses <code>Properties.propertyNames()</code> to even catch\r
+        * default properties linked into the original Properties instance.\r
+        * @param props the Properties instance to merge (may be <code>null</code>)\r
+        * @param map the target Map to merge the properties into\r
+        */\r
+       public static void mergePropertiesIntoMap(Properties props, Map map) {\r
+               if (map == null) {\r
+                       throw new IllegalArgumentException("Map must not be null");\r
+               }\r
+               if (props != null) {\r
+                       for (Enumeration en = props.propertyNames(); en.hasMoreElements();) {\r
+                               String key = (String) en.nextElement();\r
+                               map.put(key, props.getProperty(key));\r
+                       }\r
+               }\r
+       }\r
+\r
+\r
+       /**\r
+        * Check whether the given Iterator contains the given element.\r
+        * @param iterator the Iterator to check\r
+        * @param element the element to look for\r
+        * @return <code>true</code> if found, <code>false</code> else\r
+        */\r
+       public static boolean contains(Iterator iterator, Object element) {\r
+               if (iterator != null) {\r
+                       while (iterator.hasNext()) {\r
+                               Object candidate = iterator.next();\r
+                               if (ObjectUtils.nullSafeEquals(candidate, element)) {\r
+                                       return true;\r
+                               }\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * Check whether the given Enumeration contains the given element.\r
+        * @param enumeration the Enumeration to check\r
+        * @param element the element to look for\r
+        * @return <code>true</code> if found, <code>false</code> else\r
+        */\r
+       public static boolean contains(Enumeration enumeration, Object element) {\r
+               if (enumeration != null) {\r
+                       while (enumeration.hasMoreElements()) {\r
+                               Object candidate = enumeration.nextElement();\r
+                               if (ObjectUtils.nullSafeEquals(candidate, element)) {\r
+                                       return true;\r
+                               }\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * Check whether the given Collection contains the given element instance.\r
+        * <p>Enforces the given instance to be present, rather than returning\r
+        * <code>true</code> for an equal element as well.\r
+        * @param collection the Collection to check\r
+        * @param element the element to look for\r
+        * @return <code>true</code> if found, <code>false</code> else\r
+        */\r
+       public static boolean containsInstance(Collection collection, Object element) {\r
+               if (collection != null) {\r
+                       for (Iterator it = collection.iterator(); it.hasNext();) {\r
+                               Object candidate = it.next();\r
+                               if (candidate == element) {\r
+                                       return true;\r
+                               }\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * Return <code>true</code> if any element in '<code>candidates</code>' is\r
+        * contained in '<code>source</code>'; otherwise returns <code>false</code>.\r
+        * @param source the source Collection\r
+        * @param candidates the candidates to search for\r
+        * @return whether any of the candidates has been found\r
+        */\r
+       public static boolean containsAny(Collection source, Collection candidates) {\r
+               if (isEmpty(source) || isEmpty(candidates)) {\r
+                       return false;\r
+               }\r
+               for (Iterator it = candidates.iterator(); it.hasNext();) {\r
+                       if (source.contains(it.next())) {\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * Return the first element in '<code>candidates</code>' that is contained in\r
+        * '<code>source</code>'. If no element in '<code>candidates</code>' is present in\r
+        * '<code>source</code>' returns <code>null</code>. Iteration order is\r
+        * {@link Collection} implementation specific.\r
+        * @param source the source Collection\r
+        * @param candidates the candidates to search for\r
+        * @return the first present object, or <code>null</code> if not found\r
+        */\r
+       public static Object findFirstMatch(Collection source, Collection candidates) {\r
+               if (isEmpty(source) || isEmpty(candidates)) {\r
+                       return null;\r
+               }\r
+               for (Iterator it = candidates.iterator(); it.hasNext();) {\r
+                       Object candidate = it.next();\r
+                       if (source.contains(candidate)) {\r
+                               return candidate;\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+\r
+       /**\r
+        * Find a single value of the given type in the given Collection.\r
+        * @param collection the Collection to search\r
+        * @param type the type to look for\r
+        * @return a value of the given type found if there is a clear match,\r
+        * or <code>null</code> if none or more than one such value found\r
+        */\r
+       public static Object findValueOfType(Collection collection, Class type) {\r
+               if (isEmpty(collection)) {\r
+                       return null;\r
+               }\r
+               Object value = null;\r
+               for (Iterator it = collection.iterator(); it.hasNext();) {\r
+                       Object obj = it.next();\r
+                       if (type == null || type.isInstance(obj)) {\r
+                               if (value != null) {\r
+                                       // More than one value found... no clear single value.\r
+                                       return null;\r
+                               }\r
+                               value = obj;\r
+                       }\r
+               }\r
+               return value;\r
+       }\r
+\r
+       /**\r
+        * Find a single value of one of the given types in the given Collection:\r
+        * searching the Collection for a value of the first type, then\r
+        * searching for a value of the second type, etc.\r
+        * @param collection the collection to search\r
+        * @param types the types to look for, in prioritized order\r
+        * @return a value of one of the given types found if there is a clear match,\r
+        * or <code>null</code> if none or more than one such value found\r
+        */\r
+       public static Object findValueOfType(Collection collection, Class[] types) {\r
+               if (isEmpty(collection) || ObjectUtils.isEmpty(types)) {\r
+                       return null;\r
+               }\r
+               for (int i = 0; i < types.length; i++) {\r
+                       Object value = findValueOfType(collection, types[i]);\r
+                       if (value != null) {\r
+                               return value;\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+\r
+       /**\r
+        * Determine whether the given Collection only contains a single unique object.\r
+        * @param collection the Collection to check\r
+        * @return <code>true</code> if the collection contains a single reference or\r
+        * multiple references to the same instance, <code>false</code> else\r
+        */\r
+       public static boolean hasUniqueObject(Collection collection) {\r
+               if (isEmpty(collection)) {\r
+                       return false;\r
+               }\r
+               boolean hasCandidate = false;\r
+               Object candidate = null;\r
+               for (Iterator it = collection.iterator(); it.hasNext();) {\r
+                       Object elem = it.next();\r
+                       if (!hasCandidate) {\r
+                               hasCandidate = true;\r
+                               candidate = elem;\r
+                       }\r
+                       else if (candidate != elem) {\r
+                               return false;\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+\r
+}\r
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/ObjectUtils.java b/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/ObjectUtils.java
new file mode 100644 (file)
index 0000000..223b2a1
--- /dev/null
@@ -0,0 +1,833 @@
+/*\r
+ * Copyright 2002-2007 the original author or authors.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.argeo.osgi.boot.internal.springutil;\r
+\r
+import java.lang.reflect.Array;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Miscellaneous object utility methods. Mainly for internal use within the\r
+ * framework; consider Jakarta's Commons Lang for a more comprehensive suite\r
+ * of object utilities.\r
+ *\r
+ * @author Juergen Hoeller\r
+ * @author Keith Donald\r
+ * @author Rod Johnson\r
+ * @author Rob Harrop\r
+ * @author Alex Ruiz\r
+ * @since 19.03.2004\r
+ * @see org.apache.commons.lang.ObjectUtils\r
+ */\r
+public abstract class ObjectUtils {\r
+\r
+       private static final int INITIAL_HASH = 7;\r
+       private static final int MULTIPLIER = 31;\r
+\r
+       private static final String EMPTY_STRING = "";\r
+       private static final String NULL_STRING = "null";\r
+       private static final String ARRAY_START = "{";\r
+       private static final String ARRAY_END = "}";\r
+       private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END;\r
+       private static final String ARRAY_ELEMENT_SEPARATOR = ", ";\r
+\r
+\r
+       /**\r
+        * Return whether the given throwable is a checked exception:\r
+        * that is, neither a RuntimeException nor an Error.\r
+        * @param ex the throwable to check\r
+        * @return whether the throwable is a checked exception\r
+        * @see java.lang.Exception\r
+        * @see java.lang.RuntimeException\r
+        * @see java.lang.Error\r
+        */\r
+       public static boolean isCheckedException(Throwable ex) {\r
+               return !(ex instanceof RuntimeException || ex instanceof Error);\r
+       }\r
+\r
+       /**\r
+        * Check whether the given exception is compatible with the exceptions\r
+        * declared in a throws clause.\r
+        * @param ex the exception to checked\r
+        * @param declaredExceptions the exceptions declared in the throws clause\r
+        * @return whether the given exception is compatible\r
+        */\r
+       public static boolean isCompatibleWithThrowsClause(Throwable ex, Class[] declaredExceptions) {\r
+               if (!isCheckedException(ex)) {\r
+                       return true;\r
+               }\r
+               if (declaredExceptions != null) {\r
+                       for (int i = 0; i < declaredExceptions.length; i++) {\r
+                               if (declaredExceptions[i].isAssignableFrom(ex.getClass())) {\r
+                                       return true;\r
+                               }\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * Return whether the given array is empty: that is, <code>null</code>\r
+        * or of zero length.\r
+        * @param array the array to check\r
+        * @return whether the given array is empty\r
+        */\r
+       public static boolean isEmpty(Object[] array) {\r
+               return (array == null || array.length == 0);\r
+       }\r
+\r
+       /**\r
+        * Check whether the given array contains the given element.\r
+        * @param array the array to check (may be <code>null</code>,\r
+        * in which case the return value will always be <code>false</code>)\r
+        * @param element the element to check for\r
+        * @return whether the element has been found in the given array\r
+        */\r
+       public static boolean containsElement(Object[] array, Object element) {\r
+               if (array == null) {\r
+                       return false;\r
+               }\r
+               for (int i = 0; i < array.length; i++) {\r
+                       if (nullSafeEquals(array[i], element)) {\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * Append the given Object to the given array, returning a new array\r
+        * consisting of the input array contents plus the given Object.\r
+        * @param array the array to append to (can be <code>null</code>)\r
+        * @param obj the Object to append\r
+        * @return the new array (of the same component type; never <code>null</code>)\r
+        */\r
+       public static Object[] addObjectToArray(Object[] array, Object obj) {\r
+               Class compType = Object.class;\r
+               if (array != null) {\r
+                       compType = array.getClass().getComponentType();\r
+               }\r
+               else if (obj != null) {\r
+                       compType = obj.getClass();\r
+               }\r
+               int newArrLength = (array != null ? array.length + 1 : 1);\r
+               Object[] newArr = (Object[]) Array.newInstance(compType, newArrLength);\r
+               if (array != null) {\r
+                       System.arraycopy(array, 0, newArr, 0, array.length);\r
+               }\r
+               newArr[newArr.length - 1] = obj;\r
+               return newArr;\r
+       }\r
+\r
+       /**\r
+        * Convert the given array (which may be a primitive array) to an\r
+        * object array (if necessary of primitive wrapper objects).\r
+        * <p>A <code>null</code> source value will be converted to an\r
+        * empty Object array.\r
+        * @param source the (potentially primitive) array\r
+        * @return the corresponding object array (never <code>null</code>)\r
+        * @throws IllegalArgumentException if the parameter is not an array\r
+        */\r
+       public static Object[] toObjectArray(Object source) {\r
+               if (source instanceof Object[]) {\r
+                       return (Object[]) source;\r
+               }\r
+               if (source == null) {\r
+                       return new Object[0];\r
+               }\r
+               if (!source.getClass().isArray()) {\r
+                       throw new IllegalArgumentException("Source is not an array: " + source);\r
+               }\r
+               int length = Array.getLength(source);\r
+               if (length == 0) {\r
+                       return new Object[0];\r
+               }\r
+               Class wrapperType = Array.get(source, 0).getClass();\r
+               Object[] newArray = (Object[]) Array.newInstance(wrapperType, length);\r
+               for (int i = 0; i < length; i++) {\r
+                       newArray[i] = Array.get(source, i);\r
+               }\r
+               return newArray;\r
+       }\r
+\r
+\r
+       //---------------------------------------------------------------------\r
+       // Convenience methods for content-based equality/hash-code handling\r
+       //---------------------------------------------------------------------\r
+\r
+       /**\r
+        * Determine if the given objects are equal, returning <code>true</code>\r
+        * if both are <code>null</code> or <code>false</code> if only one is\r
+        * <code>null</code>.\r
+        * <p>Compares arrays with <code>Arrays.equals</code>, performing an equality\r
+        * check based on the array elements rather than the array reference.\r
+        * @param o1 first Object to compare\r
+        * @param o2 second Object to compare\r
+        * @return whether the given objects are equal\r
+        * @see java.util.Arrays#equals\r
+        */\r
+       public static boolean nullSafeEquals(Object o1, Object o2) {\r
+               if (o1 == o2) {\r
+                       return true;\r
+               }\r
+               if (o1 == null || o2 == null) {\r
+                       return false;\r
+               }\r
+               if (o1.equals(o2)) {\r
+                       return true;\r
+               }\r
+               if (o1.getClass().isArray() && o2.getClass().isArray()) {\r
+                       if (o1 instanceof Object[] && o2 instanceof Object[]) {\r
+                               return Arrays.equals((Object[]) o1, (Object[]) o2);\r
+                       }\r
+                       if (o1 instanceof boolean[] && o2 instanceof boolean[]) {\r
+                               return Arrays.equals((boolean[]) o1, (boolean[]) o2);\r
+                       }\r
+                       if (o1 instanceof byte[] && o2 instanceof byte[]) {\r
+                               return Arrays.equals((byte[]) o1, (byte[]) o2);\r
+                       }\r
+                       if (o1 instanceof char[] && o2 instanceof char[]) {\r
+                               return Arrays.equals((char[]) o1, (char[]) o2);\r
+                       }\r
+                       if (o1 instanceof double[] && o2 instanceof double[]) {\r
+                               return Arrays.equals((double[]) o1, (double[]) o2);\r
+                       }\r
+                       if (o1 instanceof float[] && o2 instanceof float[]) {\r
+                               return Arrays.equals((float[]) o1, (float[]) o2);\r
+                       }\r
+                       if (o1 instanceof int[] && o2 instanceof int[]) {\r
+                               return Arrays.equals((int[]) o1, (int[]) o2);\r
+                       }\r
+                       if (o1 instanceof long[] && o2 instanceof long[]) {\r
+                               return Arrays.equals((long[]) o1, (long[]) o2);\r
+                       }\r
+                       if (o1 instanceof short[] && o2 instanceof short[]) {\r
+                               return Arrays.equals((short[]) o1, (short[]) o2);\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * Return as hash code for the given object; typically the value of\r
+        * <code>{@link Object#hashCode()}</code>. If the object is an array,\r
+        * this method will delegate to any of the <code>nullSafeHashCode</code>\r
+        * methods for arrays in this class. If the object is <code>null</code>,\r
+        * this method returns 0.\r
+        * @see #nullSafeHashCode(Object[])\r
+        * @see #nullSafeHashCode(boolean[])\r
+        * @see #nullSafeHashCode(byte[])\r
+        * @see #nullSafeHashCode(char[])\r
+        * @see #nullSafeHashCode(double[])\r
+        * @see #nullSafeHashCode(float[])\r
+        * @see #nullSafeHashCode(int[])\r
+        * @see #nullSafeHashCode(long[])\r
+        * @see #nullSafeHashCode(short[])\r
+        */\r
+       public static int nullSafeHashCode(Object obj) {\r
+               if (obj == null) {\r
+                       return 0;\r
+               }\r
+               if (obj.getClass().isArray()) {\r
+                       if (obj instanceof Object[]) {\r
+                               return nullSafeHashCode((Object[]) obj);\r
+                       }\r
+                       if (obj instanceof boolean[]) {\r
+                               return nullSafeHashCode((boolean[]) obj);\r
+                       }\r
+                       if (obj instanceof byte[]) {\r
+                               return nullSafeHashCode((byte[]) obj);\r
+                       }\r
+                       if (obj instanceof char[]) {\r
+                               return nullSafeHashCode((char[]) obj);\r
+                       }\r
+                       if (obj instanceof double[]) {\r
+                               return nullSafeHashCode((double[]) obj);\r
+                       }\r
+                       if (obj instanceof float[]) {\r
+                               return nullSafeHashCode((float[]) obj);\r
+                       }\r
+                       if (obj instanceof int[]) {\r
+                               return nullSafeHashCode((int[]) obj);\r
+                       }\r
+                       if (obj instanceof long[]) {\r
+                               return nullSafeHashCode((long[]) obj);\r
+                       }\r
+                       if (obj instanceof short[]) {\r
+                               return nullSafeHashCode((short[]) obj);\r
+                       }\r
+               }\r
+               return obj.hashCode();\r
+       }\r
+\r
+       /**\r
+        * Return a hash code based on the contents of the specified array.\r
+        * If <code>array</code> is <code>null</code>, this method returns 0.\r
+        */\r
+       public static int nullSafeHashCode(Object[] array) {\r
+               if (array == null) {\r
+                       return 0;\r
+               }\r
+               int hash = INITIAL_HASH;\r
+               int arraySize = array.length;\r
+               for (int i = 0; i < arraySize; i++) {\r
+                       hash = MULTIPLIER * hash + nullSafeHashCode(array[i]);\r
+               }\r
+               return hash;\r
+       }\r
+\r
+       /**\r
+        * Return a hash code based on the contents of the specified array.\r
+        * If <code>array</code> is <code>null</code>, this method returns 0.\r
+        */\r
+       public static int nullSafeHashCode(boolean[] array) {\r
+               if (array == null) {\r
+                       return 0;\r
+               }\r
+               int hash = INITIAL_HASH;\r
+               int arraySize = array.length;\r
+               for (int i = 0; i < arraySize; i++) {\r
+                       hash = MULTIPLIER * hash + hashCode(array[i]);\r
+               }\r
+               return hash;\r
+       }\r
+\r
+       /**\r
+        * Return a hash code based on the contents of the specified array.\r
+        * If <code>array</code> is <code>null</code>, this method returns 0.\r
+        */\r
+       public static int nullSafeHashCode(byte[] array) {\r
+               if (array == null) {\r
+                       return 0;\r
+               }\r
+               int hash = INITIAL_HASH;\r
+               int arraySize = array.length;\r
+               for (int i = 0; i < arraySize; i++) {\r
+                       hash = MULTIPLIER * hash + array[i];\r
+               }\r
+               return hash;\r
+       }\r
+\r
+       /**\r
+        * Return a hash code based on the contents of the specified array.\r
+        * If <code>array</code> is <code>null</code>, this method returns 0.\r
+        */\r
+       public static int nullSafeHashCode(char[] array) {\r
+               if (array == null) {\r
+                       return 0;\r
+               }\r
+               int hash = INITIAL_HASH;\r
+               int arraySize = array.length;\r
+               for (int i = 0; i < arraySize; i++) {\r
+                       hash = MULTIPLIER * hash + array[i];\r
+               }\r
+               return hash;\r
+       }\r
+\r
+       /**\r
+        * Return a hash code based on the contents of the specified array.\r
+        * If <code>array</code> is <code>null</code>, this method returns 0.\r
+        */\r
+       public static int nullSafeHashCode(double[] array) {\r
+               if (array == null) {\r
+                       return 0;\r
+               }\r
+               int hash = INITIAL_HASH;\r
+               int arraySize = array.length;\r
+               for (int i = 0; i < arraySize; i++) {\r
+                       hash = MULTIPLIER * hash + hashCode(array[i]);\r
+               }\r
+               return hash;\r
+       }\r
+\r
+       /**\r
+        * Return a hash code based on the contents of the specified array.\r
+        * If <code>array</code> is <code>null</code>, this method returns 0.\r
+        */\r
+       public static int nullSafeHashCode(float[] array) {\r
+               if (array == null) {\r
+                       return 0;\r
+               }\r
+               int hash = INITIAL_HASH;\r
+               int arraySize = array.length;\r
+               for (int i = 0; i < arraySize; i++) {\r
+                       hash = MULTIPLIER * hash + hashCode(array[i]);\r
+               }\r
+               return hash;\r
+       }\r
+\r
+       /**\r
+        * Return a hash code based on the contents of the specified array.\r
+        * If <code>array</code> is <code>null</code>, this method returns 0.\r
+        */\r
+       public static int nullSafeHashCode(int[] array) {\r
+               if (array == null) {\r
+                       return 0;\r
+               }\r
+               int hash = INITIAL_HASH;\r
+               int arraySize = array.length;\r
+               for (int i = 0; i < arraySize; i++) {\r
+                       hash = MULTIPLIER * hash + array[i];\r
+               }\r
+               return hash;\r
+       }\r
+\r
+       /**\r
+        * Return a hash code based on the contents of the specified array.\r
+        * If <code>array</code> is <code>null</code>, this method returns 0.\r
+        */\r
+       public static int nullSafeHashCode(long[] array) {\r
+               if (array == null) {\r
+                       return 0;\r
+               }\r
+               int hash = INITIAL_HASH;\r
+               int arraySize = array.length;\r
+               for (int i = 0; i < arraySize; i++) {\r
+                       hash = MULTIPLIER * hash + hashCode(array[i]);\r
+               }\r
+               return hash;\r
+       }\r
+\r
+       /**\r
+        * Return a hash code based on the contents of the specified array.\r
+        * If <code>array</code> is <code>null</code>, this method returns 0.\r
+        */\r
+       public static int nullSafeHashCode(short[] array) {\r
+               if (array == null) {\r
+                       return 0;\r
+               }\r
+               int hash = INITIAL_HASH;\r
+               int arraySize = array.length;\r
+               for (int i = 0; i < arraySize; i++) {\r
+                       hash = MULTIPLIER * hash + array[i];\r
+               }\r
+               return hash;\r
+       }\r
+\r
+       /**\r
+        * Return the same value as <code>{@link Boolean#hashCode()}</code>.\r
+        * @see Boolean#hashCode()\r
+        */\r
+       public static int hashCode(boolean bool) {\r
+               return bool ? 1231 : 1237;\r
+       }\r
+\r
+       /**\r
+        * Return the same value as <code>{@link Double#hashCode()}</code>.\r
+        * @see Double#hashCode()\r
+        */\r
+       public static int hashCode(double dbl) {\r
+               long bits = Double.doubleToLongBits(dbl);\r
+               return hashCode(bits);\r
+       }\r
+\r
+       /**\r
+        * Return the same value as <code>{@link Float#hashCode()}</code>.\r
+        * @see Float#hashCode()\r
+        */\r
+       public static int hashCode(float flt) {\r
+               return Float.floatToIntBits(flt);\r
+       }\r
+\r
+       /**\r
+        * Return the same value as <code>{@link Long#hashCode()}</code>.\r
+        * @see Long#hashCode()\r
+        */\r
+       public static int hashCode(long lng) {\r
+               return (int) (lng ^ (lng >>> 32));\r
+       }\r
+\r
+\r
+       //---------------------------------------------------------------------\r
+       // Convenience methods for toString output\r
+       //---------------------------------------------------------------------\r
+\r
+       /**\r
+        * Return a String representation of an object's overall identity.\r
+        * @param obj the object (may be <code>null</code>)\r
+        * @return the object's identity as String representation,\r
+        * or an empty String if the object was <code>null</code>\r
+        */\r
+       public static String identityToString(Object obj) {\r
+               if (obj == null) {\r
+                       return EMPTY_STRING;\r
+               }\r
+               return obj.getClass().getName() + "@" + getIdentityHexString(obj);\r
+       }\r
+\r
+       /**\r
+        * Return a hex String form of an object's identity hash code.\r
+        * @param obj the object\r
+        * @return the object's identity code in hex notation\r
+        */\r
+       public static String getIdentityHexString(Object obj) {\r
+               return Integer.toHexString(System.identityHashCode(obj));\r
+       }\r
+\r
+       /**\r
+        * Return a content-based String representation if <code>obj</code> is\r
+        * not <code>null</code>; otherwise returns an empty String.\r
+        * <p>Differs from {@link #nullSafeToString(Object)} in that it returns\r
+        * an empty String rather than "null" for a <code>null</code> value.\r
+        * @param obj the object to build a display String for\r
+        * @return a display String representation of <code>obj</code>\r
+        * @see #nullSafeToString(Object)\r
+        */\r
+       public static String getDisplayString(Object obj) {\r
+               if (obj == null) {\r
+                       return EMPTY_STRING;\r
+               }\r
+               return nullSafeToString(obj);\r
+       }\r
+\r
+       /**\r
+        * Determine the class name for the given object.\r
+        * <p>Returns <code>"null"</code> if <code>obj</code> is <code>null</code>.\r
+        * @param obj the object to introspect (may be <code>null</code>)\r
+        * @return the corresponding class name\r
+        */\r
+       public static String nullSafeClassName(Object obj) {\r
+               return (obj != null ? obj.getClass().getName() : NULL_STRING);\r
+       }\r
+\r
+       /**\r
+        * Return a String representation of the specified Object.\r
+        * <p>Builds a String representation of the contents in case of an array.\r
+        * Returns <code>"null"</code> if <code>obj</code> is <code>null</code>.\r
+        * @param obj the object to build a String representation for\r
+        * @return a String representation of <code>obj</code>\r
+        */\r
+       public static String nullSafeToString(Object obj) {\r
+               if (obj == null) {\r
+                       return NULL_STRING;\r
+               }\r
+               if (obj instanceof String) {\r
+                       return (String) obj;\r
+               }\r
+               if (obj instanceof Object[]) {\r
+                       return nullSafeToString((Object[]) obj);\r
+               }\r
+               if (obj instanceof boolean[]) {\r
+                       return nullSafeToString((boolean[]) obj);\r
+               }\r
+               if (obj instanceof byte[]) {\r
+                       return nullSafeToString((byte[]) obj);\r
+               }\r
+               if (obj instanceof char[]) {\r
+                       return nullSafeToString((char[]) obj);\r
+               }\r
+               if (obj instanceof double[]) {\r
+                       return nullSafeToString((double[]) obj);\r
+               }\r
+               if (obj instanceof float[]) {\r
+                       return nullSafeToString((float[]) obj);\r
+               }\r
+               if (obj instanceof int[]) {\r
+                       return nullSafeToString((int[]) obj);\r
+               }\r
+               if (obj instanceof long[]) {\r
+                       return nullSafeToString((long[]) obj);\r
+               }\r
+               if (obj instanceof short[]) {\r
+                       return nullSafeToString((short[]) obj);\r
+               }\r
+               String str = obj.toString();\r
+               return (str != null ? str : EMPTY_STRING);\r
+       }\r
+\r
+       /**\r
+        * Return a String representation of the contents of the specified array.\r
+        * <p>The String representation consists of a list of the array's elements,\r
+        * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
+        * by the characters <code>", "</code> (a comma followed by a space). Returns\r
+        * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
+        * @param array the array to build a String representation for\r
+        * @return a String representation of <code>array</code>\r
+        */\r
+       public static String nullSafeToString(Object[] array) {\r
+               if (array == null) {\r
+                       return NULL_STRING;\r
+               }\r
+               int length = array.length;\r
+               if (length == 0) {\r
+                       return EMPTY_ARRAY;\r
+               }\r
+               StringBuffer buffer = new StringBuffer();\r
+               for (int i = 0; i < length; i++) {\r
+                       if (i == 0) {\r
+                               buffer.append(ARRAY_START);\r
+                       }\r
+                       else {\r
+                               buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
+                       }\r
+                       buffer.append(String.valueOf(array[i]));\r
+               }\r
+               buffer.append(ARRAY_END);\r
+               return buffer.toString();\r
+       }\r
+\r
+       /**\r
+        * Return a String representation of the contents of the specified array.\r
+        * <p>The String representation consists of a list of the array's elements,\r
+        * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
+        * by the characters <code>", "</code> (a comma followed by a space). Returns\r
+        * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
+        * @param array the array to build a String representation for\r
+        * @return a String representation of <code>array</code>\r
+        */\r
+       public static String nullSafeToString(boolean[] array) {\r
+               if (array == null) {\r
+                       return NULL_STRING;\r
+               }\r
+               int length = array.length;\r
+               if (length == 0) {\r
+                       return EMPTY_ARRAY;\r
+               }\r
+               StringBuffer buffer = new StringBuffer();\r
+               for (int i = 0; i < length; i++) {\r
+                       if (i == 0) {\r
+                               buffer.append(ARRAY_START);\r
+                       }\r
+                       else {\r
+                               buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
+                       }\r
+\r
+                       buffer.append(array[i]);\r
+               }\r
+               buffer.append(ARRAY_END);\r
+               return buffer.toString();\r
+       }\r
+\r
+       /**\r
+        * Return a String representation of the contents of the specified array.\r
+        * <p>The String representation consists of a list of the array's elements,\r
+        * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
+        * by the characters <code>", "</code> (a comma followed by a space). Returns\r
+        * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
+        * @param array the array to build a String representation for\r
+        * @return a String representation of <code>array</code>\r
+        */\r
+       public static String nullSafeToString(byte[] array) {\r
+               if (array == null) {\r
+                       return NULL_STRING;\r
+               }\r
+               int length = array.length;\r
+               if (length == 0) {\r
+                       return EMPTY_ARRAY;\r
+               }\r
+               StringBuffer buffer = new StringBuffer();\r
+               for (int i = 0; i < length; i++) {\r
+                       if (i == 0) {\r
+                               buffer.append(ARRAY_START);\r
+                       }\r
+                       else {\r
+                               buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
+                       }\r
+                       buffer.append(array[i]);\r
+               }\r
+               buffer.append(ARRAY_END);\r
+               return buffer.toString();\r
+       }\r
+\r
+       /**\r
+        * Return a String representation of the contents of the specified array.\r
+        * <p>The String representation consists of a list of the array's elements,\r
+        * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
+        * by the characters <code>", "</code> (a comma followed by a space). Returns\r
+        * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
+        * @param array the array to build a String representation for\r
+        * @return a String representation of <code>array</code>\r
+        */\r
+       public static String nullSafeToString(char[] array) {\r
+               if (array == null) {\r
+                       return NULL_STRING;\r
+               }\r
+               int length = array.length;\r
+               if (length == 0) {\r
+                       return EMPTY_ARRAY;\r
+               }\r
+               StringBuffer buffer = new StringBuffer();\r
+               for (int i = 0; i < length; i++) {\r
+                       if (i == 0) {\r
+                               buffer.append(ARRAY_START);\r
+                       }\r
+                       else {\r
+                               buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
+                       }\r
+                       buffer.append("'").append(array[i]).append("'");\r
+               }\r
+               buffer.append(ARRAY_END);\r
+               return buffer.toString();\r
+       }\r
+\r
+       /**\r
+        * Return a String representation of the contents of the specified array.\r
+        * <p>The String representation consists of a list of the array's elements,\r
+        * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
+        * by the characters <code>", "</code> (a comma followed by a space). Returns\r
+        * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
+        * @param array the array to build a String representation for\r
+        * @return a String representation of <code>array</code>\r
+        */\r
+       public static String nullSafeToString(double[] array) {\r
+               if (array == null) {\r
+                       return NULL_STRING;\r
+               }\r
+               int length = array.length;\r
+               if (length == 0) {\r
+                       return EMPTY_ARRAY;\r
+               }\r
+               StringBuffer buffer = new StringBuffer();\r
+               for (int i = 0; i < length; i++) {\r
+                       if (i == 0) {\r
+                               buffer.append(ARRAY_START);\r
+                       }\r
+                       else {\r
+                               buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
+                       }\r
+\r
+                       buffer.append(array[i]);\r
+               }\r
+               buffer.append(ARRAY_END);\r
+               return buffer.toString();\r
+       }\r
+\r
+       /**\r
+        * Return a String representation of the contents of the specified array.\r
+        * <p>The String representation consists of a list of the array's elements,\r
+        * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
+        * by the characters <code>", "</code> (a comma followed by a space). Returns\r
+        * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
+        * @param array the array to build a String representation for\r
+        * @return a String representation of <code>array</code>\r
+        */\r
+       public static String nullSafeToString(float[] array) {\r
+               if (array == null) {\r
+                       return NULL_STRING;\r
+               }\r
+               int length = array.length;\r
+               if (length == 0) {\r
+                       return EMPTY_ARRAY;\r
+               }\r
+               StringBuffer buffer = new StringBuffer();\r
+               for (int i = 0; i < length; i++) {\r
+                       if (i == 0) {\r
+                               buffer.append(ARRAY_START);\r
+                       }\r
+                       else {\r
+                               buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
+                       }\r
+\r
+                       buffer.append(array[i]);\r
+               }\r
+               buffer.append(ARRAY_END);\r
+               return buffer.toString();\r
+       }\r
+\r
+       /**\r
+        * Return a String representation of the contents of the specified array.\r
+        * <p>The String representation consists of a list of the array's elements,\r
+        * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
+        * by the characters <code>", "</code> (a comma followed by a space). Returns\r
+        * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
+        * @param array the array to build a String representation for\r
+        * @return a String representation of <code>array</code>\r
+        */\r
+       public static String nullSafeToString(int[] array) {\r
+               if (array == null) {\r
+                       return NULL_STRING;\r
+               }\r
+               int length = array.length;\r
+               if (length == 0) {\r
+                       return EMPTY_ARRAY;\r
+               }\r
+               StringBuffer buffer = new StringBuffer();\r
+               for (int i = 0; i < length; i++) {\r
+                       if (i == 0) {\r
+                               buffer.append(ARRAY_START);\r
+                       }\r
+                       else {\r
+                               buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
+                       }\r
+                       buffer.append(array[i]);\r
+               }\r
+               buffer.append(ARRAY_END);\r
+               return buffer.toString();\r
+       }\r
+\r
+       /**\r
+        * Return a String representation of the contents of the specified array.\r
+        * <p>The String representation consists of a list of the array's elements,\r
+        * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
+        * by the characters <code>", "</code> (a comma followed by a space). Returns\r
+        * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
+        * @param array the array to build a String representation for\r
+        * @return a String representation of <code>array</code>\r
+        */\r
+       public static String nullSafeToString(long[] array) {\r
+               if (array == null) {\r
+                       return NULL_STRING;\r
+               }\r
+               int length = array.length;\r
+               if (length == 0) {\r
+                       return EMPTY_ARRAY;\r
+               }\r
+               StringBuffer buffer = new StringBuffer();\r
+               for (int i = 0; i < length; i++) {\r
+                       if (i == 0) {\r
+                               buffer.append(ARRAY_START);\r
+                       }\r
+                       else {\r
+                               buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
+                       }\r
+                       buffer.append(array[i]);\r
+               }\r
+               buffer.append(ARRAY_END);\r
+               return buffer.toString();\r
+       }\r
+\r
+       /**\r
+        * Return a String representation of the contents of the specified array.\r
+        * <p>The String representation consists of a list of the array's elements,\r
+        * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
+        * by the characters <code>", "</code> (a comma followed by a space). Returns\r
+        * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
+        * @param array the array to build a String representation for\r
+        * @return a String representation of <code>array</code>\r
+        */\r
+       public static String nullSafeToString(short[] array) {\r
+               if (array == null) {\r
+                       return NULL_STRING;\r
+               }\r
+               int length = array.length;\r
+               if (length == 0) {\r
+                       return EMPTY_ARRAY;\r
+               }\r
+               StringBuffer buffer = new StringBuffer();\r
+               for (int i = 0; i < length; i++) {\r
+                       if (i == 0) {\r
+                               buffer.append(ARRAY_START);\r
+                       }\r
+                       else {\r
+                               buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
+                       }\r
+                       buffer.append(array[i]);\r
+               }\r
+               buffer.append(ARRAY_END);\r
+               return buffer.toString();\r
+       }\r
+\r
+}\r
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/PathMatcher.java b/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/PathMatcher.java
new file mode 100644 (file)
index 0000000..d7a2322
--- /dev/null
@@ -0,0 +1,91 @@
+/*\r
+ * Copyright 2002-2007 the original author or authors.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.argeo.osgi.boot.internal.springutil;\r
+\r
+/**\r
+ * Strategy interface for <code>String</code>-based path matching.\r
+ * \r
+ * <p>Used by {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver},\r
+ * {@link org.springframework.web.servlet.handler.AbstractUrlHandlerMapping},\r
+ * {@link org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver},\r
+ * and {@link org.springframework.web.servlet.mvc.WebContentInterceptor}.\r
+ *\r
+ * <p>The default implementation is {@link AntPathMatcher}, supporting the\r
+ * Ant-style pattern syntax.\r
+ *\r
+ * @author Juergen Hoeller\r
+ * @since 1.2\r
+ * @see AntPathMatcher\r
+ */\r
+public interface PathMatcher {\r
+\r
+       /**\r
+        * Does the given <code>path</code> represent a pattern that can be matched\r
+        * by an implementation of this interface?\r
+        * <p>If the return value is <code>false</code>, then the {@link #match}\r
+        * method does not have to be used because direct equality comparisons\r
+        * on the static path Strings will lead to the same result.\r
+        * @param path the path String to check\r
+        * @return <code>true</code> if the given <code>path</code> represents a pattern\r
+        */\r
+       boolean isPattern(String path);\r
+\r
+       /**\r
+        * Match the given <code>path</code> against the given <code>pattern</code>,\r
+        * according to this PathMatcher's matching strategy.\r
+        * @param pattern the pattern to match against\r
+        * @param path the path String to test\r
+        * @return <code>true</code> if the supplied <code>path</code> matched,\r
+        * <code>false</code> if it didn't\r
+        */\r
+       boolean match(String pattern, String path);\r
+\r
+       /**\r
+        * Match the given <code>path</code> against the corresponding part of the given\r
+        * <code>pattern</code>, according to this PathMatcher's matching strategy.\r
+        * <p>Determines whether the pattern at least matches as far as the given base\r
+        * path goes, assuming that a full path may then match as well.\r
+        * @param pattern the pattern to match against\r
+        * @param path the path String to test\r
+        * @return <code>true</code> if the supplied <code>path</code> matched,\r
+        * <code>false</code> if it didn't\r
+        */\r
+       boolean matchStart(String pattern, String path);\r
+\r
+       /**\r
+        * Given a pattern and a full path, determine the pattern-mapped part.\r
+        * <p>This method is supposed to find out which part of the path is matched\r
+        * dynamically through an actual pattern, that is, it strips off a statically\r
+        * defined leading path from the given full path, returning only the actually\r
+        * pattern-matched part of the path.\r
+        * <p>For example: For "myroot/*.html" as pattern and "myroot/myfile.html"\r
+        * as full path, this method should return "myfile.html". The detailed\r
+        * determination rules are specified to this PathMatcher's matching strategy.\r
+        * <p>A simple implementation may return the given full path as-is in case\r
+        * of an actual pattern, and the empty String in case of the pattern not\r
+        * containing any dynamic parts (i.e. the <code>pattern</code> parameter being\r
+        * a static path that wouldn't qualify as an actual {@link #isPattern pattern}).\r
+        * A sophisticated implementation will differentiate between the static parts\r
+        * and the dynamic parts of the given path pattern.\r
+        * @param pattern the path pattern\r
+        * @param path the full path to introspect\r
+        * @return the pattern-mapped part of the given <code>path</code>\r
+        * (never <code>null</code>)\r
+        */\r
+       String extractPathWithinPattern(String pattern, String path);\r
+\r
+}\r
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/StringUtils.java b/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/StringUtils.java
new file mode 100644 (file)
index 0000000..2626590
--- /dev/null
@@ -0,0 +1,1113 @@
+/*\r
+ * Copyright 2002-2008 the original author or authors.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.argeo.osgi.boot.internal.springutil;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Enumeration;\r
+import java.util.Iterator;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.Locale;\r
+import java.util.Properties;\r
+import java.util.Set;\r
+import java.util.StringTokenizer;\r
+import java.util.TreeSet;\r
+\r
+/**\r
+ * Miscellaneous {@link String} utility methods.\r
+ *\r
+ * <p>Mainly for internal use within the framework; consider\r
+ * <a href="http://jakarta.apache.org/commons/lang/">Jakarta's Commons Lang</a>\r
+ * for a more comprehensive suite of String utilities.\r
+ *\r
+ * <p>This class delivers some simple functionality that should really\r
+ * be provided by the core Java <code>String</code> and {@link StringBuffer}\r
+ * classes, such as the ability to {@link #replace} all occurrences of a given\r
+ * substring in a target string. It also provides easy-to-use methods to convert\r
+ * between delimited strings, such as CSV strings, and collections and arrays.\r
+ *\r
+ * @author Rod Johnson\r
+ * @author Juergen Hoeller\r
+ * @author Keith Donald\r
+ * @author Rob Harrop\r
+ * @author Rick Evans\r
+ * @since 16 April 2001\r
+ * @see org.apache.commons.lang.StringUtils\r
+ */\r
+public abstract class StringUtils {\r
+\r
+       private static final String FOLDER_SEPARATOR = "/";\r
+\r
+       private static final String WINDOWS_FOLDER_SEPARATOR = "\\";\r
+\r
+       private static final String TOP_PATH = "..";\r
+\r
+       private static final String CURRENT_PATH = ".";\r
+\r
+       private static final char EXTENSION_SEPARATOR = '.';\r
+\r
+\r
+       //---------------------------------------------------------------------\r
+       // General convenience methods for working with Strings\r
+       //---------------------------------------------------------------------\r
+\r
+       /**\r
+        * Check that the given CharSequence is neither <code>null</code> nor of length 0.\r
+        * Note: Will return <code>true</code> for a CharSequence that purely consists of whitespace.\r
+        * <p><pre>\r
+        * StringUtils.hasLength(null) = false\r
+        * StringUtils.hasLength("") = false\r
+        * StringUtils.hasLength(" ") = true\r
+        * StringUtils.hasLength("Hello") = true\r
+        * </pre>\r
+        * @param str the CharSequence to check (may be <code>null</code>)\r
+        * @return <code>true</code> if the CharSequence is not null and has length\r
+        * @see #hasText(String)\r
+        */\r
+       public static boolean hasLength(CharSequence str) {\r
+               return (str != null && str.length() > 0);\r
+       }\r
+\r
+       /**\r
+        * Check that the given String is neither <code>null</code> nor of length 0.\r
+        * Note: Will return <code>true</code> for a String that purely consists of whitespace.\r
+        * @param str the String to check (may be <code>null</code>)\r
+        * @return <code>true</code> if the String is not null and has length\r
+        * @see #hasLength(CharSequence)\r
+        */\r
+       public static boolean hasLength(String str) {\r
+               return hasLength((CharSequence) str);\r
+       }\r
+\r
+       /**\r
+        * Check whether the given CharSequence has actual text.\r
+        * More specifically, returns <code>true</code> if the string not <code>null</code>,\r
+        * its length is greater than 0, and it contains at least one non-whitespace character.\r
+        * <p><pre>\r
+        * StringUtils.hasText(null) = false\r
+        * StringUtils.hasText("") = false\r
+        * StringUtils.hasText(" ") = false\r
+        * StringUtils.hasText("12345") = true\r
+        * StringUtils.hasText(" 12345 ") = true\r
+        * </pre>\r
+        * @param str the CharSequence to check (may be <code>null</code>)\r
+        * @return <code>true</code> if the CharSequence is not <code>null</code>,\r
+        * its length is greater than 0, and it does not contain whitespace only\r
+        * @see java.lang.Character#isWhitespace\r
+        */\r
+       public static boolean hasText(CharSequence str) {\r
+               if (!hasLength(str)) {\r
+                       return false;\r
+               }\r
+               int strLen = str.length();\r
+               for (int i = 0; i < strLen; i++) {\r
+                       if (!Character.isWhitespace(str.charAt(i))) {\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * Check whether the given String has actual text.\r
+        * More specifically, returns <code>true</code> if the string not <code>null</code>,\r
+        * its length is greater than 0, and it contains at least one non-whitespace character.\r
+        * @param str the String to check (may be <code>null</code>)\r
+        * @return <code>true</code> if the String is not <code>null</code>, its length is\r
+        * greater than 0, and it does not contain whitespace only\r
+        * @see #hasText(CharSequence)\r
+        */\r
+       public static boolean hasText(String str) {\r
+               return hasText((CharSequence) str);\r
+       }\r
+\r
+       /**\r
+        * Check whether the given CharSequence contains any whitespace characters.\r
+        * @param str the CharSequence to check (may be <code>null</code>)\r
+        * @return <code>true</code> if the CharSequence is not empty and\r
+        * contains at least 1 whitespace character\r
+        * @see java.lang.Character#isWhitespace\r
+        */\r
+       public static boolean containsWhitespace(CharSequence str) {\r
+               if (!hasLength(str)) {\r
+                       return false;\r
+               }\r
+               int strLen = str.length();\r
+               for (int i = 0; i < strLen; i++) {\r
+                       if (Character.isWhitespace(str.charAt(i))) {\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * Check whether the given String contains any whitespace characters.\r
+        * @param str the String to check (may be <code>null</code>)\r
+        * @return <code>true</code> if the String is not empty and\r
+        * contains at least 1 whitespace character\r
+        * @see #containsWhitespace(CharSequence)\r
+        */\r
+       public static boolean containsWhitespace(String str) {\r
+               return containsWhitespace((CharSequence) str);\r
+       }\r
+\r
+       /**\r
+        * Trim leading and trailing whitespace from the given String.\r
+        * @param str the String to check\r
+        * @return the trimmed String\r
+        * @see java.lang.Character#isWhitespace\r
+        */\r
+       public static String trimWhitespace(String str) {\r
+               if (!hasLength(str)) {\r
+                       return str;\r
+               }\r
+               StringBuffer buf = new StringBuffer(str);\r
+               while (buf.length() > 0 && Character.isWhitespace(buf.charAt(0))) {\r
+                       buf.deleteCharAt(0);\r
+               }\r
+               while (buf.length() > 0 && Character.isWhitespace(buf.charAt(buf.length() - 1))) {\r
+                       buf.deleteCharAt(buf.length() - 1);\r
+               }\r
+               return buf.toString();\r
+       }\r
+\r
+       /**\r
+        * Trim <i>all</i> whitespace from the given String:\r
+        * leading, trailing, and inbetween characters.\r
+        * @param str the String to check\r
+        * @return the trimmed String\r
+        * @see java.lang.Character#isWhitespace\r
+        */\r
+       public static String trimAllWhitespace(String str) {\r
+               if (!hasLength(str)) {\r
+                       return str;\r
+               }\r
+               StringBuffer buf = new StringBuffer(str);\r
+               int index = 0;\r
+               while (buf.length() > index) {\r
+                       if (Character.isWhitespace(buf.charAt(index))) {\r
+                               buf.deleteCharAt(index);\r
+                       }\r
+                       else {\r
+                               index++;\r
+                       }\r
+               }\r
+               return buf.toString();\r
+       }\r
+\r
+       /**\r
+        * Trim leading whitespace from the given String.\r
+        * @param str the String to check\r
+        * @return the trimmed String\r
+        * @see java.lang.Character#isWhitespace\r
+        */\r
+       public static String trimLeadingWhitespace(String str) {\r
+               if (!hasLength(str)) {\r
+                       return str;\r
+               }\r
+               StringBuffer buf = new StringBuffer(str);\r
+               while (buf.length() > 0 && Character.isWhitespace(buf.charAt(0))) {\r
+                       buf.deleteCharAt(0);\r
+               }\r
+               return buf.toString();\r
+       }\r
+\r
+       /**\r
+        * Trim trailing whitespace from the given String.\r
+        * @param str the String to check\r
+        * @return the trimmed String\r
+        * @see java.lang.Character#isWhitespace\r
+        */\r
+       public static String trimTrailingWhitespace(String str) {\r
+               if (!hasLength(str)) {\r
+                       return str;\r
+               }\r
+               StringBuffer buf = new StringBuffer(str);\r
+               while (buf.length() > 0 && Character.isWhitespace(buf.charAt(buf.length() - 1))) {\r
+                       buf.deleteCharAt(buf.length() - 1);\r
+               }\r
+               return buf.toString();\r
+       }\r
+\r
+       /**\r
+        * Trim all occurences of the supplied leading character from the given String.\r
+        * @param str the String to check\r
+        * @param leadingCharacter the leading character to be trimmed\r
+        * @return the trimmed String\r
+        */\r
+       public static String trimLeadingCharacter(String str, char leadingCharacter) {\r
+               if (!hasLength(str)) {\r
+                       return str;\r
+               }\r
+               StringBuffer buf = new StringBuffer(str);\r
+               while (buf.length() > 0 && buf.charAt(0) == leadingCharacter) {\r
+                       buf.deleteCharAt(0);\r
+               }\r
+               return buf.toString();\r
+       }\r
+\r
+       /**\r
+        * Trim all occurences of the supplied trailing character from the given String.\r
+        * @param str the String to check\r
+        * @param trailingCharacter the trailing character to be trimmed\r
+        * @return the trimmed String\r
+        */\r
+       public static String trimTrailingCharacter(String str, char trailingCharacter) {\r
+               if (!hasLength(str)) {\r
+                       return str;\r
+               }\r
+               StringBuffer buf = new StringBuffer(str);\r
+               while (buf.length() > 0 && buf.charAt(buf.length() - 1) == trailingCharacter) {\r
+                       buf.deleteCharAt(buf.length() - 1);\r
+               }\r
+               return buf.toString();\r
+       }\r
+\r
+\r
+       /**\r
+        * Test if the given String starts with the specified prefix,\r
+        * ignoring upper/lower case.\r
+        * @param str the String to check\r
+        * @param prefix the prefix to look for\r
+        * @see java.lang.String#startsWith\r
+        */\r
+       public static boolean startsWithIgnoreCase(String str, String prefix) {\r
+               if (str == null || prefix == null) {\r
+                       return false;\r
+               }\r
+               if (str.startsWith(prefix)) {\r
+                       return true;\r
+               }\r
+               if (str.length() < prefix.length()) {\r
+                       return false;\r
+               }\r
+               String lcStr = str.substring(0, prefix.length()).toLowerCase();\r
+               String lcPrefix = prefix.toLowerCase();\r
+               return lcStr.equals(lcPrefix);\r
+       }\r
+\r
+       /**\r
+        * Test if the given String ends with the specified suffix,\r
+        * ignoring upper/lower case.\r
+        * @param str the String to check\r
+        * @param suffix the suffix to look for\r
+        * @see java.lang.String#endsWith\r
+        */\r
+       public static boolean endsWithIgnoreCase(String str, String suffix) {\r
+               if (str == null || suffix == null) {\r
+                       return false;\r
+               }\r
+               if (str.endsWith(suffix)) {\r
+                       return true;\r
+               }\r
+               if (str.length() < suffix.length()) {\r
+                       return false;\r
+               }\r
+\r
+               String lcStr = str.substring(str.length() - suffix.length()).toLowerCase();\r
+               String lcSuffix = suffix.toLowerCase();\r
+               return lcStr.equals(lcSuffix);\r
+       }\r
+\r
+       /**\r
+        * Test whether the given string matches the given substring\r
+        * at the given index.\r
+        * @param str the original string (or StringBuffer)\r
+        * @param index the index in the original string to start matching against\r
+        * @param substring the substring to match at the given index\r
+        */\r
+       public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {\r
+               for (int j = 0; j < substring.length(); j++) {\r
+                       int i = index + j;\r
+                       if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {\r
+                               return false;\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * Count the occurrences of the substring in string s.\r
+        * @param str string to search in. Return 0 if this is null.\r
+        * @param sub string to search for. Return 0 if this is null.\r
+        */\r
+       public static int countOccurrencesOf(String str, String sub) {\r
+               if (str == null || sub == null || str.length() == 0 || sub.length() == 0) {\r
+                       return 0;\r
+               }\r
+               int count = 0, pos = 0, idx = 0;\r
+               while ((idx = str.indexOf(sub, pos)) != -1) {\r
+                       ++count;\r
+                       pos = idx + sub.length();\r
+               }\r
+               return count;\r
+       }\r
+\r
+       /**\r
+        * Replace all occurences of a substring within a string with\r
+        * another string.\r
+        * @param inString String to examine\r
+        * @param oldPattern String to replace\r
+        * @param newPattern String to insert\r
+        * @return a String with the replacements\r
+        */\r
+       public static String replace(String inString, String oldPattern, String newPattern) {\r
+               if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) {\r
+                       return inString;\r
+               }\r
+               StringBuffer sbuf = new StringBuffer();\r
+               // output StringBuffer we'll build up\r
+               int pos = 0; // our position in the old string\r
+               int index = inString.indexOf(oldPattern);\r
+               // the index of an occurrence we've found, or -1\r
+               int patLen = oldPattern.length();\r
+               while (index >= 0) {\r
+                       sbuf.append(inString.substring(pos, index));\r
+                       sbuf.append(newPattern);\r
+                       pos = index + patLen;\r
+                       index = inString.indexOf(oldPattern, pos);\r
+               }\r
+               sbuf.append(inString.substring(pos));\r
+               // remember to append any characters to the right of a match\r
+               return sbuf.toString();\r
+       }\r
+\r
+       /**\r
+        * Delete all occurrences of the given substring.\r
+        * @param inString the original String\r
+        * @param pattern the pattern to delete all occurrences of\r
+        * @return the resulting String\r
+        */\r
+       public static String delete(String inString, String pattern) {\r
+               return replace(inString, pattern, "");\r
+       }\r
+\r
+       /**\r
+        * Delete any character in a given String.\r
+        * @param inString the original String\r
+        * @param charsToDelete a set of characters to delete.\r
+        * E.g. "az\n" will delete 'a's, 'z's and new lines.\r
+        * @return the resulting String\r
+        */\r
+       public static String deleteAny(String inString, String charsToDelete) {\r
+               if (!hasLength(inString) || !hasLength(charsToDelete)) {\r
+                       return inString;\r
+               }\r
+               StringBuffer out = new StringBuffer();\r
+               for (int i = 0; i < inString.length(); i++) {\r
+                       char c = inString.charAt(i);\r
+                       if (charsToDelete.indexOf(c) == -1) {\r
+                               out.append(c);\r
+                       }\r
+               }\r
+               return out.toString();\r
+       }\r
+\r
+\r
+       //---------------------------------------------------------------------\r
+       // Convenience methods for working with formatted Strings\r
+       //---------------------------------------------------------------------\r
+\r
+       /**\r
+        * Quote the given String with single quotes.\r
+        * @param str the input String (e.g. "myString")\r
+        * @return the quoted String (e.g. "'myString'"),\r
+        * or <code>null<code> if the input was <code>null</code>\r
+        */\r
+       public static String quote(String str) {\r
+               return (str != null ? "'" + str + "'" : null);\r
+       }\r
+\r
+       /**\r
+        * Turn the given Object into a String with single quotes\r
+        * if it is a String; keeping the Object as-is else.\r
+        * @param obj the input Object (e.g. "myString")\r
+        * @return the quoted String (e.g. "'myString'"),\r
+        * or the input object as-is if not a String\r
+        */\r
+       public static Object quoteIfString(Object obj) {\r
+               return (obj instanceof String ? quote((String) obj) : obj);\r
+       }\r
+\r
+       /**\r
+        * Unqualify a string qualified by a '.' dot character. For example,\r
+        * "this.name.is.qualified", returns "qualified".\r
+        * @param qualifiedName the qualified name\r
+        */\r
+       public static String unqualify(String qualifiedName) {\r
+               return unqualify(qualifiedName, '.');\r
+       }\r
+\r
+       /**\r
+        * Unqualify a string qualified by a separator character. For example,\r
+        * "this:name:is:qualified" returns "qualified" if using a ':' separator.\r
+        * @param qualifiedName the qualified name\r
+        * @param separator the separator\r
+        */\r
+       public static String unqualify(String qualifiedName, char separator) {\r
+               return qualifiedName.substring(qualifiedName.lastIndexOf(separator) + 1);\r
+       }\r
+\r
+       /**\r
+        * Capitalize a <code>String</code>, changing the first letter to\r
+        * upper case as per {@link Character#toUpperCase(char)}.\r
+        * No other letters are changed.\r
+        * @param str the String to capitalize, may be <code>null</code>\r
+        * @return the capitalized String, <code>null</code> if null\r
+        */\r
+       public static String capitalize(String str) {\r
+               return changeFirstCharacterCase(str, true);\r
+       }\r
+\r
+       /**\r
+        * Uncapitalize a <code>String</code>, changing the first letter to\r
+        * lower case as per {@link Character#toLowerCase(char)}.\r
+        * No other letters are changed.\r
+        * @param str the String to uncapitalize, may be <code>null</code>\r
+        * @return the uncapitalized String, <code>null</code> if null\r
+        */\r
+       public static String uncapitalize(String str) {\r
+               return changeFirstCharacterCase(str, false);\r
+       }\r
+\r
+       private static String changeFirstCharacterCase(String str, boolean capitalize) {\r
+               if (str == null || str.length() == 0) {\r
+                       return str;\r
+               }\r
+               StringBuffer buf = new StringBuffer(str.length());\r
+               if (capitalize) {\r
+                       buf.append(Character.toUpperCase(str.charAt(0)));\r
+               }\r
+               else {\r
+                       buf.append(Character.toLowerCase(str.charAt(0)));\r
+               }\r
+               buf.append(str.substring(1));\r
+               return buf.toString();\r
+       }\r
+\r
+       /**\r
+        * Extract the filename from the given path,\r
+        * e.g. "mypath/myfile.txt" -> "myfile.txt".\r
+        * @param path the file path (may be <code>null</code>)\r
+        * @return the extracted filename, or <code>null</code> if none\r
+        */\r
+       public static String getFilename(String path) {\r
+               if (path == null) {\r
+                       return null;\r
+               }\r
+               int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);\r
+               return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path);\r
+       }\r
+\r
+       /**\r
+        * Extract the filename extension from the given path,\r
+        * e.g. "mypath/myfile.txt" -> "txt".\r
+        * @param path the file path (may be <code>null</code>)\r
+        * @return the extracted filename extension, or <code>null</code> if none\r
+        */\r
+       public static String getFilenameExtension(String path) {\r
+               if (path == null) {\r
+                       return null;\r
+               }\r
+               int sepIndex = path.lastIndexOf(EXTENSION_SEPARATOR);\r
+               return (sepIndex != -1 ? path.substring(sepIndex + 1) : null);\r
+       }\r
+\r
+       /**\r
+        * Strip the filename extension from the given path,\r
+        * e.g. "mypath/myfile.txt" -> "mypath/myfile".\r
+        * @param path the file path (may be <code>null</code>)\r
+        * @return the path with stripped filename extension,\r
+        * or <code>null</code> if none\r
+        */\r
+       public static String stripFilenameExtension(String path) {\r
+               if (path == null) {\r
+                       return null;\r
+               }\r
+               int sepIndex = path.lastIndexOf(EXTENSION_SEPARATOR);\r
+               return (sepIndex != -1 ? path.substring(0, sepIndex) : path);\r
+       }\r
+\r
+       /**\r
+        * Apply the given relative path to the given path,\r
+        * assuming standard Java folder separation (i.e. "/" separators);\r
+        * @param path the path to start from (usually a full file path)\r
+        * @param relativePath the relative path to apply\r
+        * (relative to the full file path above)\r
+        * @return the full file path that results from applying the relative path\r
+        */\r
+       public static String applyRelativePath(String path, String relativePath) {\r
+               int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);\r
+               if (separatorIndex != -1) {\r
+                       String newPath = path.substring(0, separatorIndex);\r
+                       if (!relativePath.startsWith(FOLDER_SEPARATOR)) {\r
+                               newPath += FOLDER_SEPARATOR;\r
+                       }\r
+                       return newPath + relativePath;\r
+               }\r
+               else {\r
+                       return relativePath;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Normalize the path by suppressing sequences like "path/.." and\r
+        * inner simple dots.\r
+        * <p>The result is convenient for path comparison. For other uses,\r
+        * notice that Windows separators ("\") are replaced by simple slashes.\r
+        * @param path the original path\r
+        * @return the normalized path\r
+        */\r
+       public static String cleanPath(String path) {\r
+               if (path == null) {\r
+                       return null;\r
+               }\r
+               String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR);\r
+\r
+               // Strip prefix from path to analyze, to not treat it as part of the\r
+               // first path element. This is necessary to correctly parse paths like\r
+               // "file:core/../core/io/Resource.class", where the ".." should just\r
+               // strip the first "core" directory while keeping the "file:" prefix.\r
+               int prefixIndex = pathToUse.indexOf(":");\r
+               String prefix = "";\r
+               if (prefixIndex != -1) {\r
+                       prefix = pathToUse.substring(0, prefixIndex + 1);\r
+                       pathToUse = pathToUse.substring(prefixIndex + 1);\r
+               }\r
+               if (pathToUse.startsWith(FOLDER_SEPARATOR)) {\r
+                       prefix = prefix + FOLDER_SEPARATOR;\r
+                       pathToUse = pathToUse.substring(1);\r
+               }\r
+\r
+               String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR);\r
+               List pathElements = new LinkedList();\r
+               int tops = 0;\r
+\r
+               for (int i = pathArray.length - 1; i >= 0; i--) {\r
+                       String element = pathArray[i];\r
+                       if (CURRENT_PATH.equals(element)) {\r
+                               // Points to current directory - drop it.\r
+                       }\r
+                       else if (TOP_PATH.equals(element)) {\r
+                               // Registering top path found.\r
+                               tops++;\r
+                       }\r
+                       else {\r
+                               if (tops > 0) {\r
+                                       // Merging path element with element corresponding to top path.\r
+                                       tops--;\r
+                               }\r
+                               else {\r
+                                       // Normal path element found.\r
+                                       pathElements.add(0, element);\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // Remaining top paths need to be retained.\r
+               for (int i = 0; i < tops; i++) {\r
+                       pathElements.add(0, TOP_PATH);\r
+               }\r
+\r
+               return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);\r
+       }\r
+\r
+       /**\r
+        * Compare two paths after normalization of them.\r
+        * @param path1 first path for comparison\r
+        * @param path2 second path for comparison\r
+        * @return whether the two paths are equivalent after normalization\r
+        */\r
+       public static boolean pathEquals(String path1, String path2) {\r
+               return cleanPath(path1).equals(cleanPath(path2));\r
+       }\r
+\r
+       /**\r
+        * Parse the given <code>localeString</code> into a {@link Locale}.\r
+        * <p>This is the inverse operation of {@link Locale#toString Locale's toString}.\r
+        * @param localeString the locale string, following <code>Locale's</code>\r
+        * <code>toString()</code> format ("en", "en_UK", etc);\r
+        * also accepts spaces as separators, as an alternative to underscores\r
+        * @return a corresponding <code>Locale</code> instance\r
+        */\r
+       public static Locale parseLocaleString(String localeString) {\r
+               String[] parts = tokenizeToStringArray(localeString, "_ ", false, false);\r
+               String language = (parts.length > 0 ? parts[0] : "");\r
+               String country = (parts.length > 1 ? parts[1] : "");\r
+               String variant = "";\r
+               if (parts.length >= 2) {\r
+                       // There is definitely a variant, and it is everything after the country\r
+                       // code sans the separator between the country code and the variant.\r
+                       int endIndexOfCountryCode = localeString.indexOf(country) + country.length();\r
+                       // Strip off any leading '_' and whitespace, what's left is the variant.\r
+                       variant = trimLeadingWhitespace(localeString.substring(endIndexOfCountryCode));\r
+                       if (variant.startsWith("_")) {\r
+                               variant = trimLeadingCharacter(variant, '_');\r
+                       }\r
+               }\r
+               return (language.length() > 0 ? new Locale(language, country, variant) : null);\r
+       }\r
+\r
+       /**\r
+        * Determine the RFC 3066 compliant language tag,\r
+        * as used for the HTTP "Accept-Language" header.\r
+        * @param locale the Locale to transform to a language tag\r
+        * @return the RFC 3066 compliant language tag as String\r
+        */\r
+       public static String toLanguageTag(Locale locale) {\r
+               return locale.getLanguage() + (hasText(locale.getCountry()) ? "-" + locale.getCountry() : "");\r
+       }\r
+\r
+\r
+       //---------------------------------------------------------------------\r
+       // Convenience methods for working with String arrays\r
+       //---------------------------------------------------------------------\r
+\r
+       /**\r
+        * Append the given String to the given String array, returning a new array\r
+        * consisting of the input array contents plus the given String.\r
+        * @param array the array to append to (can be <code>null</code>)\r
+        * @param str the String to append\r
+        * @return the new array (never <code>null</code>)\r
+        */\r
+       public static String[] addStringToArray(String[] array, String str) {\r
+               if (ObjectUtils.isEmpty(array)) {\r
+                       return new String[] {str};\r
+               }\r
+               String[] newArr = new String[array.length + 1];\r
+               System.arraycopy(array, 0, newArr, 0, array.length);\r
+               newArr[array.length] = str;\r
+               return newArr;\r
+       }\r
+\r
+       /**\r
+        * Concatenate the given String arrays into one,\r
+        * with overlapping array elements included twice.\r
+        * <p>The order of elements in the original arrays is preserved.\r
+        * @param array1 the first array (can be <code>null</code>)\r
+        * @param array2 the second array (can be <code>null</code>)\r
+        * @return the new array (<code>null</code> if both given arrays were <code>null</code>)\r
+        */\r
+       public static String[] concatenateStringArrays(String[] array1, String[] array2) {\r
+               if (ObjectUtils.isEmpty(array1)) {\r
+                       return array2;\r
+               }\r
+               if (ObjectUtils.isEmpty(array2)) {\r
+                       return array1;\r
+               }\r
+               String[] newArr = new String[array1.length + array2.length];\r
+               System.arraycopy(array1, 0, newArr, 0, array1.length);\r
+               System.arraycopy(array2, 0, newArr, array1.length, array2.length);\r
+               return newArr;\r
+       }\r
+\r
+       /**\r
+        * Merge the given String arrays into one, with overlapping\r
+        * array elements only included once.\r
+        * <p>The order of elements in the original arrays is preserved\r
+        * (with the exception of overlapping elements, which are only\r
+        * included on their first occurence).\r
+        * @param array1 the first array (can be <code>null</code>)\r
+        * @param array2 the second array (can be <code>null</code>)\r
+        * @return the new array (<code>null</code> if both given arrays were <code>null</code>)\r
+        */\r
+       public static String[] mergeStringArrays(String[] array1, String[] array2) {\r
+               if (ObjectUtils.isEmpty(array1)) {\r
+                       return array2;\r
+               }\r
+               if (ObjectUtils.isEmpty(array2)) {\r
+                       return array1;\r
+               }\r
+               List result = new ArrayList();\r
+               result.addAll(Arrays.asList(array1));\r
+               for (int i = 0; i < array2.length; i++) {\r
+                       String str = array2[i];\r
+                       if (!result.contains(str)) {\r
+                               result.add(str);\r
+                       }\r
+               }\r
+               return toStringArray(result);\r
+       }\r
+\r
+       /**\r
+        * Turn given source String array into sorted array.\r
+        * @param array the source array\r
+        * @return the sorted array (never <code>null</code>)\r
+        */\r
+       public static String[] sortStringArray(String[] array) {\r
+               if (ObjectUtils.isEmpty(array)) {\r
+                       return new String[0];\r
+               }\r
+               Arrays.sort(array);\r
+               return array;\r
+       }\r
+\r
+       /**\r
+        * Copy the given Collection into a String array.\r
+        * The Collection must contain String elements only.\r
+        * @param collection the Collection to copy\r
+        * @return the String array (<code>null</code> if the passed-in\r
+        * Collection was <code>null</code>)\r
+        */\r
+       public static String[] toStringArray(Collection collection) {\r
+               if (collection == null) {\r
+                       return null;\r
+               }\r
+               return (String[]) collection.toArray(new String[collection.size()]);\r
+       }\r
+\r
+       /**\r
+        * Copy the given Enumeration into a String array.\r
+        * The Enumeration must contain String elements only.\r
+        * @param enumeration the Enumeration to copy\r
+        * @return the String array (<code>null</code> if the passed-in\r
+        * Enumeration was <code>null</code>)\r
+        */\r
+       public static String[] toStringArray(Enumeration enumeration) {\r
+               if (enumeration == null) {\r
+                       return null;\r
+               }\r
+               List list = Collections.list(enumeration);\r
+               return (String[]) list.toArray(new String[list.size()]);\r
+       }\r
+\r
+       /**\r
+        * Trim the elements of the given String array,\r
+        * calling <code>String.trim()</code> on each of them.\r
+        * @param array the original String array\r
+        * @return the resulting array (of the same size) with trimmed elements\r
+        */\r
+       public static String[] trimArrayElements(String[] array) {\r
+               if (ObjectUtils.isEmpty(array)) {\r
+                       return new String[0];\r
+               }\r
+               String[] result = new String[array.length];\r
+               for (int i = 0; i < array.length; i++) {\r
+                       String element = array[i];\r
+                       result[i] = (element != null ? element.trim() : null);\r
+               }\r
+               return result;\r
+       }\r
+\r
+       /**\r
+        * Remove duplicate Strings from the given array.\r
+        * Also sorts the array, as it uses a TreeSet.\r
+        * @param array the String array\r
+        * @return an array without duplicates, in natural sort order\r
+        */\r
+       public static String[] removeDuplicateStrings(String[] array) {\r
+               if (ObjectUtils.isEmpty(array)) {\r
+                       return array;\r
+               }\r
+               Set set = new TreeSet();\r
+               for (int i = 0; i < array.length; i++) {\r
+                       set.add(array[i]);\r
+               }\r
+               return toStringArray(set);\r
+       }\r
+\r
+       /**\r
+        * Split a String at the first occurrence of the delimiter.\r
+        * Does not include the delimiter in the result.\r
+        * @param toSplit the string to split\r
+        * @param delimiter to split the string up with\r
+        * @return a two element array with index 0 being before the delimiter, and\r
+        * index 1 being after the delimiter (neither element includes the delimiter);\r
+        * or <code>null</code> if the delimiter wasn't found in the given input String\r
+        */\r
+       public static String[] split(String toSplit, String delimiter) {\r
+               if (!hasLength(toSplit) || !hasLength(delimiter)) {\r
+                       return null;\r
+               }\r
+               int offset = toSplit.indexOf(delimiter);\r
+               if (offset < 0) {\r
+                       return null;\r
+               }\r
+               String beforeDelimiter = toSplit.substring(0, offset);\r
+               String afterDelimiter = toSplit.substring(offset + delimiter.length());\r
+               return new String[] {beforeDelimiter, afterDelimiter};\r
+       }\r
+\r
+       /**\r
+        * Take an array Strings and split each element based on the given delimiter.\r
+        * A <code>Properties</code> instance is then generated, with the left of the\r
+        * delimiter providing the key, and the right of the delimiter providing the value.\r
+        * <p>Will trim both the key and value before adding them to the\r
+        * <code>Properties</code> instance.\r
+        * @param array the array to process\r
+        * @param delimiter to split each element using (typically the equals symbol)\r
+        * @return a <code>Properties</code> instance representing the array contents,\r
+        * or <code>null</code> if the array to process was null or empty\r
+        */\r
+       public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) {\r
+               return splitArrayElementsIntoProperties(array, delimiter, null);\r
+       }\r
+\r
+       /**\r
+        * Take an array Strings and split each element based on the given delimiter.\r
+        * A <code>Properties</code> instance is then generated, with the left of the\r
+        * delimiter providing the key, and the right of the delimiter providing the value.\r
+        * <p>Will trim both the key and value before adding them to the\r
+        * <code>Properties</code> instance.\r
+        * @param array the array to process\r
+        * @param delimiter to split each element using (typically the equals symbol)\r
+        * @param charsToDelete one or more characters to remove from each element\r
+        * prior to attempting the split operation (typically the quotation mark\r
+        * symbol), or <code>null</code> if no removal should occur\r
+        * @return a <code>Properties</code> instance representing the array contents,\r
+        * or <code>null</code> if the array to process was <code>null</code> or empty\r
+        */\r
+       public static Properties splitArrayElementsIntoProperties(\r
+                       String[] array, String delimiter, String charsToDelete) {\r
+\r
+               if (ObjectUtils.isEmpty(array)) {\r
+                       return null;\r
+               }\r
+               Properties result = new Properties();\r
+               for (int i = 0; i < array.length; i++) {\r
+                       String element = array[i];\r
+                       if (charsToDelete != null) {\r
+                               element = deleteAny(array[i], charsToDelete);\r
+                       }\r
+                       String[] splittedElement = split(element, delimiter);\r
+                       if (splittedElement == null) {\r
+                               continue;\r
+                       }\r
+                       result.setProperty(splittedElement[0].trim(), splittedElement[1].trim());\r
+               }\r
+               return result;\r
+       }\r
+\r
+       /**\r
+        * Tokenize the given String into a String array via a StringTokenizer.\r
+        * Trims tokens and omits empty tokens.\r
+        * <p>The given delimiters string is supposed to consist of any number of\r
+        * delimiter characters. Each of those characters can be used to separate\r
+        * tokens. A delimiter is always a single character; for multi-character\r
+        * delimiters, consider using <code>delimitedListToStringArray</code>\r
+        * @param str the String to tokenize\r
+        * @param delimiters the delimiter characters, assembled as String\r
+        * (each of those characters is individually considered as delimiter).\r
+        * @return an array of the tokens\r
+        * @see java.util.StringTokenizer\r
+        * @see java.lang.String#trim()\r
+        * @see #delimitedListToStringArray\r
+        */\r
+       public static String[] tokenizeToStringArray(String str, String delimiters) {\r
+               return tokenizeToStringArray(str, delimiters, true, true);\r
+       }\r
+\r
+       /**\r
+        * Tokenize the given String into a String array via a StringTokenizer.\r
+        * <p>The given delimiters string is supposed to consist of any number of\r
+        * delimiter characters. Each of those characters can be used to separate\r
+        * tokens. A delimiter is always a single character; for multi-character\r
+        * delimiters, consider using <code>delimitedListToStringArray</code>\r
+        * @param str the String to tokenize\r
+        * @param delimiters the delimiter characters, assembled as String\r
+        * (each of those characters is individually considered as delimiter)\r
+        * @param trimTokens trim the tokens via String's <code>trim</code>\r
+        * @param ignoreEmptyTokens omit empty tokens from the result array\r
+        * (only applies to tokens that are empty after trimming; StringTokenizer\r
+        * will not consider subsequent delimiters as token in the first place).\r
+        * @return an array of the tokens (<code>null</code> if the input String\r
+        * was <code>null</code>)\r
+        * @see java.util.StringTokenizer\r
+        * @see java.lang.String#trim()\r
+        * @see #delimitedListToStringArray\r
+        */\r
+       public static String[] tokenizeToStringArray(\r
+                       String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {\r
+\r
+               if (str == null) {\r
+                       return null;\r
+               }\r
+               StringTokenizer st = new StringTokenizer(str, delimiters);\r
+               List tokens = new ArrayList();\r
+               while (st.hasMoreTokens()) {\r
+                       String token = st.nextToken();\r
+                       if (trimTokens) {\r
+                               token = token.trim();\r
+                       }\r
+                       if (!ignoreEmptyTokens || token.length() > 0) {\r
+                               tokens.add(token);\r
+                       }\r
+               }\r
+               return toStringArray(tokens);\r
+       }\r
+\r
+       /**\r
+        * Take a String which is a delimited list and convert it to a String array.\r
+        * <p>A single delimiter can consists of more than one character: It will still\r
+        * be considered as single delimiter string, rather than as bunch of potential\r
+        * delimiter characters - in contrast to <code>tokenizeToStringArray</code>.\r
+        * @param str the input String\r
+        * @param delimiter the delimiter between elements (this is a single delimiter,\r
+        * rather than a bunch individual delimiter characters)\r
+        * @return an array of the tokens in the list\r
+        * @see #tokenizeToStringArray\r
+        */\r
+       public static String[] delimitedListToStringArray(String str, String delimiter) {\r
+               return delimitedListToStringArray(str, delimiter, null);\r
+       }\r
+\r
+       /**\r
+        * Take a String which is a delimited list and convert it to a String array.\r
+        * <p>A single delimiter can consists of more than one character: It will still\r
+        * be considered as single delimiter string, rather than as bunch of potential\r
+        * delimiter characters - in contrast to <code>tokenizeToStringArray</code>.\r
+        * @param str the input String\r
+        * @param delimiter the delimiter between elements (this is a single delimiter,\r
+        * rather than a bunch individual delimiter characters)\r
+        * @param charsToDelete a set of characters to delete. Useful for deleting unwanted\r
+        * line breaks: e.g. "\r\n\f" will delete all new lines and line feeds in a String.\r
+        * @return an array of the tokens in the list\r
+        * @see #tokenizeToStringArray\r
+        */\r
+       public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) {\r
+               if (str == null) {\r
+                       return new String[0];\r
+               }\r
+               if (delimiter == null) {\r
+                       return new String[] {str};\r
+               }\r
+               List result = new ArrayList();\r
+               if ("".equals(delimiter)) {\r
+                       for (int i = 0; i < str.length(); i++) {\r
+                               result.add(deleteAny(str.substring(i, i + 1), charsToDelete));\r
+                       }\r
+               }\r
+               else {\r
+                       int pos = 0;\r
+                       int delPos = 0;\r
+                       while ((delPos = str.indexOf(delimiter, pos)) != -1) {\r
+                               result.add(deleteAny(str.substring(pos, delPos), charsToDelete));\r
+                               pos = delPos + delimiter.length();\r
+                       }\r
+                       if (str.length() > 0 && pos <= str.length()) {\r
+                               // Add rest of String, but not in case of empty input.\r
+                               result.add(deleteAny(str.substring(pos), charsToDelete));\r
+                       }\r
+               }\r
+               return toStringArray(result);\r
+       }\r
+\r
+       /**\r
+        * Convert a CSV list into an array of Strings.\r
+        * @param str the input String\r
+        * @return an array of Strings, or the empty array in case of empty input\r
+        */\r
+       public static String[] commaDelimitedListToStringArray(String str) {\r
+               return delimitedListToStringArray(str, ",");\r
+       }\r
+\r
+       /**\r
+        * Convenience method to convert a CSV string list to a set.\r
+        * Note that this will suppress duplicates.\r
+        * @param str the input String\r
+        * @return a Set of String entries in the list\r
+        */\r
+       public static Set commaDelimitedListToSet(String str) {\r
+               Set set = new TreeSet();\r
+               String[] tokens = commaDelimitedListToStringArray(str);\r
+               for (int i = 0; i < tokens.length; i++) {\r
+                       set.add(tokens[i]);\r
+               }\r
+               return set;\r
+       }\r
+\r
+       /**\r
+        * Convenience method to return a Collection as a delimited (e.g. CSV)\r
+        * String. E.g. useful for <code>toString()</code> implementations.\r
+        * @param coll the Collection to display\r
+        * @param delim the delimiter to use (probably a ",")\r
+        * @param prefix the String to start each element with\r
+        * @param suffix the String to end each element with\r
+        * @return the delimited String\r
+        */\r
+       public static String collectionToDelimitedString(Collection coll, String delim, String prefix, String suffix) {\r
+               if (CollectionUtils.isEmpty(coll)) {\r
+                       return "";\r
+               }\r
+               StringBuffer sb = new StringBuffer();\r
+               Iterator it = coll.iterator();\r
+               while (it.hasNext()) {\r
+                       sb.append(prefix).append(it.next()).append(suffix);\r
+                       if (it.hasNext()) {\r
+                               sb.append(delim);\r
+                       }\r
+               }\r
+               return sb.toString();\r
+       }\r
+\r
+       /**\r
+        * Convenience method to return a Collection as a delimited (e.g. CSV)\r
+        * String. E.g. useful for <code>toString()</code> implementations.\r
+        * @param coll the Collection to display\r
+        * @param delim the delimiter to use (probably a ",")\r
+        * @return the delimited String\r
+        */\r
+       public static String collectionToDelimitedString(Collection coll, String delim) {\r
+               return collectionToDelimitedString(coll, delim, "", "");\r
+       }\r
+\r
+       /**\r
+        * Convenience method to return a Collection as a CSV String.\r
+        * E.g. useful for <code>toString()</code> implementations.\r
+        * @param coll the Collection to display\r
+        * @return the delimited String\r
+        */\r
+       public static String collectionToCommaDelimitedString(Collection coll) {\r
+               return collectionToDelimitedString(coll, ",");\r
+       }\r
+\r
+       /**\r
+        * Convenience method to return a String array as a delimited (e.g. CSV)\r
+        * String. E.g. useful for <code>toString()</code> implementations.\r
+        * @param arr the array to display\r
+        * @param delim the delimiter to use (probably a ",")\r
+        * @return the delimited String\r
+        */\r
+       public static String arrayToDelimitedString(Object[] arr, String delim) {\r
+               if (ObjectUtils.isEmpty(arr)) {\r
+                       return "";\r
+               }\r
+               StringBuffer sb = new StringBuffer();\r
+               for (int i = 0; i < arr.length; i++) {\r
+                       if (i > 0) {\r
+                               sb.append(delim);\r
+                       }\r
+                       sb.append(arr[i]);\r
+               }\r
+               return sb.toString();\r
+       }\r
+\r
+       /**\r
+        * Convenience method to return a String array as a CSV String.\r
+        * E.g. useful for <code>toString()</code> implementations.\r
+        * @param arr the array to display\r
+        * @return the delimited String\r
+        */\r
+       public static String arrayToCommaDelimitedString(Object[] arr) {\r
+               return arrayToDelimitedString(arr, ",");\r
+       }\r
+\r
+}\r
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/SystemPropertyUtils.java b/trunk/base/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/internal/springutil/SystemPropertyUtils.java
new file mode 100644 (file)
index 0000000..f81adc2
--- /dev/null
@@ -0,0 +1,87 @@
+/*\r
+ * Copyright 2002-2008 the original author or authors.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.argeo.osgi.boot.internal.springutil;\r
+\r
+/**\r
+ * Helper class for resolving placeholders in texts. Usually applied to file paths.\r
+ *\r
+ * <p>A text may contain <code>${...}</code> placeholders, to be resolved as\r
+ * system properties: e.g. <code>${user.dir}</code>.\r
+ *\r
+ * @author Juergen Hoeller\r
+ * @since 1.2.5\r
+ * @see #PLACEHOLDER_PREFIX\r
+ * @see #PLACEHOLDER_SUFFIX\r
+ * @see System#getProperty(String)\r
+ */\r
+public abstract class SystemPropertyUtils {\r
+\r
+       /** Prefix for system property placeholders: "${" */\r
+       public static final String PLACEHOLDER_PREFIX = "${";\r
+\r
+       /** Suffix for system property placeholders: "}" */\r
+       public static final String PLACEHOLDER_SUFFIX = "}";\r
+\r
+\r
+       /**\r
+        * Resolve ${...} placeholders in the given text,\r
+        * replacing them with corresponding system property values.\r
+        * @param text the String to resolve\r
+        * @return the resolved String\r
+        * @see #PLACEHOLDER_PREFIX\r
+        * @see #PLACEHOLDER_SUFFIX\r
+        */\r
+       public static String resolvePlaceholders(String text) {\r
+               StringBuffer buf = new StringBuffer(text);\r
+\r
+               int startIndex = buf.indexOf(PLACEHOLDER_PREFIX);\r
+               while (startIndex != -1) {\r
+                       int endIndex = buf.indexOf(PLACEHOLDER_SUFFIX, startIndex + PLACEHOLDER_PREFIX.length());\r
+                       if (endIndex != -1) {\r
+                               String placeholder = buf.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);\r
+                               int nextIndex = endIndex + PLACEHOLDER_SUFFIX.length();\r
+                               try {\r
+                                       String propVal = System.getProperty(placeholder);\r
+                                       if (propVal == null) {\r
+                                               // Fall back to searching the system environment.\r
+                                               //propVal = System.getenv(placeholder);// mbaudier - 2009-07-26\r
+                                               throw new Error("getenv no longer supported, use properties and -D instead: " + placeholder);\r
+                                       }\r
+                                       if (propVal != null) {\r
+                                               buf.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), propVal);\r
+                                               nextIndex = startIndex + propVal.length();\r
+                                       }\r
+                                       else {\r
+                                               System.err.println("Could not resolve placeholder '" + placeholder + "' in [" + text +\r
+                                                               "] as system property: neither system property nor environment variable found");\r
+                                       }\r
+                               }\r
+                               catch (Throwable ex) {\r
+                                       System.err.println("Could not resolve placeholder '" + placeholder + "' in [" + text +\r
+                                                       "] as system property: " + ex);\r
+                               }\r
+                               startIndex = buf.indexOf(PLACEHOLDER_PREFIX, nextIndex);\r
+                       }\r
+                       else {\r
+                               startIndex = -1;\r
+                       }\r
+               }\r
+\r
+               return buf.toString();\r
+       }\r
+\r
+}\r
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/jars/test.jar b/trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/jars/test.jar
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/others/subdir/org.argeo.osgi.boot.test.bundle3/META-INF/MANIFEST.MF b/trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/others/subdir/org.argeo.osgi.boot.test.bundle3/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..f23da44
--- /dev/null
@@ -0,0 +1,2 @@
+Bundle-SymbolicName: org.argeo.osgi.boot.test.bundle3
+Bundle-Version: 0.1.0
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/some/excluded/org.argeo.osgi.boot.test.bundle0/META-INF/MANIFEST.MF b/trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/some/excluded/org.argeo.osgi.boot.test.bundle0/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..9344d07
--- /dev/null
@@ -0,0 +1,2 @@
+Bundle-SymbolicName: org.argeo.osgi.boot.test.bundle0
+Bundle-Version: 0.1.0
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/some/org.argeo.osgi.boot.test.bundle1/META-INF/MANIFEST.MF b/trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/some/org.argeo.osgi.boot.test.bundle1/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..0fb66dc
--- /dev/null
@@ -0,0 +1,2 @@
+Bundle-SymbolicName: org.argeo.osgi.boot.test.bundle1
+Bundle-Version: 0.1.0
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/some/org.argeo.osgi.boot.test.bundle2/META-INF/MANIFEST.MF b/trunk/base/runtime/org.argeo.osgi.boot/src/test/bundles/some/org.argeo.osgi.boot.test.bundle2/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..97ecd31
--- /dev/null
@@ -0,0 +1,2 @@
+Bundle-SymbolicName: org.argeo.osgi.boot.test.bundle2
+Bundle-Version: 0.1.0
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/test/java/org/argeo/osgi/boot/OsgiBootNoRuntimeTest.java b/trunk/base/runtime/org.argeo.osgi.boot/src/test/java/org/argeo/osgi/boot/OsgiBootNoRuntimeTest.java
new file mode 100644 (file)
index 0000000..638b13a
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.osgi.boot;
+
+import java.io.File;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.argeo.osgi.boot.OsgiBoot;
+
+/** Tests which do not require a runtime. */
+public class OsgiBootNoRuntimeTest extends TestCase {
+       public final static String BUNDLES = "src/test/bundles/some;in=*;ex=excluded,"
+                       + "src/test/bundles/others;in=**/org.argeo.*";
+
+       /** Tests that location lists are properly parsed. */
+       public void testLocations() {
+               String baseUrl = "file:";
+               String locations = "/mydir/myfile" + File.pathSeparator
+                               + "/myotherdir/myotherfile";
+
+               OsgiBoot osgiBoot = new OsgiBoot(null);
+               osgiBoot.setExcludeSvn(true);
+               List urls = osgiBoot.getLocationsUrls(baseUrl, locations);
+               assertEquals(2, urls.size());
+               assertEquals("file:/mydir/myfile", urls.get(0));
+               assertEquals("file:/myotherdir/myotherfile", urls.get(1));
+       }
+
+       /** Tests that bundle lists are properly parsed. */
+       public void testBundles() {
+               String baseUrl = "file:";
+               String bundles = BUNDLES;
+               OsgiBoot osgiBoot = new OsgiBoot(null);
+               osgiBoot.setExcludeSvn(true);
+               List urls = osgiBoot.getBundlesUrls(baseUrl, bundles);
+               for (int i = 0; i < urls.size(); i++)
+                       System.out.println(urls.get(i));
+               assertEquals(3, urls.size());
+
+               List jarUrls = osgiBoot.getBundlesUrls(baseUrl,
+                               "src/test/bundles/jars;in=*.jar");
+               for (int i = 0; i < jarUrls.size(); i++)
+                       System.out.println(jarUrls.get(i));
+               assertEquals(1, jarUrls.size());
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.osgi.boot/src/test/java/org/argeo/osgi/boot/OsgiBootRuntimeTest.java b/trunk/base/runtime/org.argeo.osgi.boot/src/test/java/org/argeo/osgi/boot/OsgiBootRuntimeTest.java
new file mode 100644 (file)
index 0000000..32b72bc
--- /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.osgi.boot;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.runtime.adaptor.EclipseStarter;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/** Starts an Equinox runtime and provision it with OSGi boot. */
+public class OsgiBootRuntimeTest extends TestCase {
+       protected OsgiBoot osgiBoot = null;
+       private boolean osgiRuntimeAlreadyRunning = false;
+
+       public void testInstallAndStart() throws Exception {
+               if (osgiRuntimeAlreadyRunning) {
+                       System.out
+                                       .println("OSGi runtime already running, skipping test...");
+                       return;
+               }
+               osgiBoot.installUrls(osgiBoot.getBundlesUrls(OsgiBoot.DEFAULT_BASE_URL,
+                               OsgiBootNoRuntimeTest.BUNDLES));
+               Map map = new TreeMap(osgiBoot.getBundlesBySymbolicName());
+               for (Iterator keys = map.keySet().iterator(); keys.hasNext();) {
+                       Object key = keys.next();
+                       Bundle bundle = (Bundle) map.get(key);
+                       System.out.println(key + " : " + bundle.getLocation());
+               }
+               assertEquals(4, map.size());
+               Iterator keys = map.keySet().iterator();
+               assertEquals("org.argeo.osgi.boot.test.bundle1", keys.next());
+               assertEquals("org.argeo.osgi.boot.test.bundle2", keys.next());
+               assertEquals("org.argeo.osgi.boot.test.bundle3", keys.next());
+               assertEquals("org.eclipse.osgi", keys.next());
+
+               osgiBoot.startBundles("org.argeo.osgi.boot.test.bundle2");
+               long begin = System.currentTimeMillis();
+               while (System.currentTimeMillis() - begin < 10000) {
+                       Map mapBundles = osgiBoot.getBundlesBySymbolicName();
+                       Bundle bundle = (Bundle) mapBundles
+                                       .get("org.argeo.osgi.boot.test.bundle2");
+                       if (bundle.getState() == Bundle.ACTIVE) {
+                               System.out.println("Bundle " + bundle + " started.");
+                               return;
+                       }
+               }
+               fail("Bundle not started after timeout limit.");
+       }
+
+       protected BundleContext startRuntime() throws Exception {
+               String[] args = { "-console", "-clean" };
+               BundleContext bundleContext = EclipseStarter.startup(args, null);
+               return bundleContext;
+       }
+
+       protected void stopRuntime() throws Exception {
+               EclipseStarter.shutdown();
+       }
+
+       public void setUp() throws Exception {
+               osgiRuntimeAlreadyRunning = EclipseStarter.isRunning();
+               if (osgiRuntimeAlreadyRunning)
+                       return;
+               BundleContext bundleContext = startRuntime();
+               osgiBoot = new OsgiBoot(bundleContext);
+       }
+
+       public void tearDown() throws Exception {
+               if (osgiRuntimeAlreadyRunning)
+                       return;
+               osgiBoot = null;
+               stopRuntime();
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.support.junit/.classpath b/trunk/base/runtime/org.argeo.support.junit/.classpath
new file mode 100644 (file)
index 0000000..3bf3ade
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/base/runtime/org.argeo.support.junit/.project b/trunk/base/runtime/org.argeo.support.junit/.project
new file mode 100644 (file)
index 0000000..cc37598
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.support.junit</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/trunk/base/runtime/org.argeo.support.junit/.settings/org.eclipse.jdt.core.prefs b/trunk/base/runtime/org.argeo.support.junit/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..383a4ec
--- /dev/null
@@ -0,0 +1,5 @@
+#Tue Oct 13 10:02:09 CEST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/base/runtime/org.argeo.support.junit/.settings/org.maven.ide.eclipse.prefs b/trunk/base/runtime/org.argeo.support.junit/.settings/org.maven.ide.eclipse.prefs
new file mode 100644 (file)
index 0000000..721e27e
--- /dev/null
@@ -0,0 +1,9 @@
+#Tue Oct 13 10:01:59 CEST 2009
+activeProfiles=
+eclipse.preferences.version=1
+fullBuildGoals=process-test-resources
+includeModules=false
+resolveWorkspaceProjects=true
+resourceFilterGoals=process-resources resources\:testResources
+skipCompilerPlugin=true
+version=1
diff --git a/trunk/base/runtime/org.argeo.support.junit/pom.xml b/trunk/base/runtime/org.argeo.support.junit/pom.xml
new file mode 100644 (file)
index 0000000..265c51d
--- /dev/null
@@ -0,0 +1,64 @@
+<?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.base</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.support.junit</artifactId>
+       <name>Commons Support JUnit</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>
+                                                       org.argeo.support.junit.*
+                                               </Export-Package>
+                                               <Import-Package>org.springframework.core.io,*</Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>junit</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.core</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.context</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.beans</artifactId>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.support.junit/src/main/java/org/argeo/support/junit/AbstractSpringTestCase.java b/trunk/base/runtime/org.argeo.support.junit/src/main/java/org/argeo/support/junit/AbstractSpringTestCase.java
new file mode 100644 (file)
index 0000000..11a93e9
--- /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.support.junit;
+
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+
+/** Helper for tests using a Spring application context. */
+public abstract class AbstractSpringTestCase extends TestCase {
+       protected final Log log = LogFactory.getLog(getClass());
+       private ConfigurableApplicationContext context;
+
+       /**
+        * Gets (and creates if necessary) the application context to use. Default
+        * implementation uses a class path xml application context and calls
+        * {@link #getApplicationContextLocation()}.
+        */
+       protected ConfigurableApplicationContext getContext() {
+               if (context == null) {
+                       context = new ClassPathXmlApplicationContext(
+                                       getApplicationContextLocation());
+                       if (getIsStartContext())
+                               context.start();
+               }
+               return context;
+       }
+
+       /** Whether the context should be started after being created. */
+       protected Boolean getIsStartContext() {
+               return false;
+       }
+
+       /** Returns a bean from the underlying context */
+       @SuppressWarnings(value = { "unchecked" })
+       protected <T> T getBean(String beanId) {
+               return (T) getContext().getBean(beanId);
+       }
+
+       protected <T> T getBean(Class<? extends T> clss) {
+               T bean = loadSingleFromContext(getContext(), clss);
+               if (bean == null) {
+                       throw new RuntimeException("Cannot retrieve a unique bean of type "
+                                       + clss);
+               } else {
+                       return bean;
+               }
+       }
+
+       /**
+        * The location of the application to load. The default implementation
+        * returns <i>applicationContext.xml</i> found in the same package as the
+        * test.
+        */
+       protected String getApplicationContextLocation() {
+               return inPackage("applicationContext.xml");
+       }
+
+       /**
+        * Prefixes the package of the class after converting the '.' to '/' in
+        * order to have a resource path.
+        */
+       protected String inPackage(String suffix) {
+               String prefix = getClass().getPackage().getName().replace('.', '/');
+               return prefix + '/' + suffix;
+       }
+
+       @SuppressWarnings(value = { "unchecked" })
+       protected <T> T loadSingleFromContext(ListableBeanFactory context,
+                       Class<T> clss) {
+               Map<String, T> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
+                               context, clss, false, false);
+               if (beans.size() == 1) {
+                       return beans.values().iterator().next();
+               } else if (beans.size() > 1) {
+                       if (log.isDebugEnabled()) {
+                               log
+                                               .debug(("Found more that one bean for type " + clss
+                                                               + ": " + beans.keySet()));
+                       }
+                       return null;
+               } else {
+                       return null;
+               }
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/.classpath b/trunk/base/runtime/org.argeo.util/.classpath
new file mode 100644 (file)
index 0000000..8499fd0
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+       <classpathentry kind="src" path="src/test/java"/>
+       <classpathentry kind="src" path="src/test/resources"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+       <classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/base/runtime/org.argeo.util/.project b/trunk/base/runtime/org.argeo.util/.project
new file mode 100644 (file)
index 0000000..171ff88
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.util</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/trunk/base/runtime/org.argeo.util/build.properties b/trunk/base/runtime/org.argeo.util/build.properties
new file mode 100644 (file)
index 0000000..fcffb0f
--- /dev/null
@@ -0,0 +1,6 @@
+source.. = src/main/java/,\
+           src/test/resources/,\
+           src/test/java/
+bin.includes = META-INF/,\
+               .
+additional.bundles = junit
diff --git a/trunk/base/runtime/org.argeo.util/pom.xml b/trunk/base/runtime/org.argeo.util/pom.xml
new file mode 100644 (file)
index 0000000..a9db98f
--- /dev/null
@@ -0,0 +1,33 @@
+<?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.base</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.util</artifactId>
+       <name>Commons Util (no third party dependencies)</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>
+                                                       org.argeo.*
+                                               </Export-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>junit</artifactId>
+                       <scope>test</scope>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/ArgeoException.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/ArgeoException.java
new file mode 100644 (file)
index 0000000..9cf9186
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+/** Argeo Commons specific exception. */
+public class ArgeoException extends RuntimeException {
+       private static final long serialVersionUID = 1L;
+
+       /** Creates an exception with a message. */
+       public ArgeoException(String message) {
+               super(message);
+       }
+
+       /** Creates an exception with a message and a root cause. */
+       public ArgeoException(String message, Throwable e) {
+               super(message, e);
+       }
+
+       /**
+        * Chain the messages of all causes (one per line, <b>starts with a line
+        * return</b>) without all the stack
+        */
+       public static String chainCausesMessages(Throwable t) {
+               StringBuffer buf = new StringBuffer();
+               chainCauseMessage(buf, t);
+               return buf.toString();
+       }
+
+       /** Recursive chaining of messages */
+       private static void chainCauseMessage(StringBuffer buf, Throwable t) {
+               buf.append('\n').append(' ').append(t.getClass().getCanonicalName())
+                               .append(": ").append(t.getMessage());
+               if (t.getCause() != null)
+                       chainCauseMessage(buf, t.getCause());
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/ArgeoLogListener.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/ArgeoLogListener.java
new file mode 100644 (file)
index 0000000..bac8a98
--- /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;
+
+/** 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/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/ArgeoLogger.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/ArgeoLogger.java
new file mode 100644 (file)
index 0000000..0657c29
--- /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;
+
+/**
+ * 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/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/ArgeoMonitor.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/ArgeoMonitor.java
new file mode 100644 (file)
index 0000000..9ef23cc
--- /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;
+
+/**
+ * Simple monitor abstraction. Inspired by Eclipse IProgressMOnitor, but without
+ * dependency to it.
+ */
+public interface 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/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/OperatingSystem.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/OperatingSystem.java
new file mode 100644 (file)
index 0000000..697c86c
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+/** The current operating system. */
+public class OperatingSystem {
+       public final static int NIX = 1;
+       public final static int WINDOWS = 2;
+       public final static int SOLARIS = 3;
+
+       public final static int os;
+       static {
+               String osName = System.getProperty("os.name");
+               if (osName.startsWith("Win"))
+                       os = WINDOWS;
+               else if (osName.startsWith("Solaris"))
+                       os = SOLARIS;
+               else
+                       os = NIX;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/StreamUtils.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/StreamUtils.java
new file mode 100644 (file)
index 0000000..39caadd
--- /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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+
+/** Utilities to be used when APache COmmons IO is not available. */
+public class StreamUtils {
+       private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
+       /*
+        * APACHE COMMONS IO (inspired)
+        */
+
+       /** @return the number of bytes */
+       public static Long copy(InputStream in, OutputStream out)
+                       throws IOException {
+               Long count = 0l;
+               byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
+               while (true) {
+                       int length = in.read(buf);
+                       if (length < 0)
+                               break;
+                       out.write(buf, 0, length);
+                       count = count + length;
+               }
+               return count;
+       }
+
+       /** @return the number of chars */
+       public static Long copy(Reader in, Writer out) throws IOException {
+               Long count = 0l;
+               char[] buf = new char[DEFAULT_BUFFER_SIZE];
+               while (true) {
+                       int length = in.read(buf);
+                       if (length < 0)
+                               break;
+                       out.write(buf, 0, length);
+                       count = count + length;
+               }
+               return count;
+       }
+
+       public static void closeQuietly(InputStream in) {
+               if (in != null)
+                       try {
+                               in.close();
+                       } catch (Exception e) {
+                               //
+                       }
+       }
+
+       public static void closeQuietly(OutputStream out) {
+               if (out != null)
+                       try {
+                               out.close();
+                       } catch (Exception e) {
+                               //
+                       }
+       }
+
+       public static void closeQuietly(Reader in) {
+               if (in != null)
+                       try {
+                               in.close();
+                       } catch (Exception e) {
+                               //
+                       }
+       }
+
+       public static void closeQuietly(Writer out) {
+               if (out != null)
+                       try {
+                               out.close();
+                       } catch (Exception e) {
+                               //
+                       }
+       }
+
+       /*
+        * APACHE COMMONS CODEC (forked)
+        */
+       /**
+        * Used to build output as Hex
+        */
+       private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5',
+                       '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+       /**
+        * Used to build output as Hex
+        */
+       private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5',
+                       '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+       /**
+        * Converts an array of bytes into a String representing the hexadecimal
+        * values of each byte in order. The returned String will be double the
+        * length of the passed array, as it takes two characters to represent any
+        * given byte.
+        * 
+        * @param data
+        *            a byte[] to convert to Hex characters
+        * @return A String containing hexadecimal characters
+        * @since 1.4
+        */
+       public static String encodeHexString(byte[] data) {
+               return new String(encodeHex(data));
+       }
+
+       /**
+        * Converts an array of bytes into an array of characters representing the
+        * hexadecimal values of each byte in order. The returned array will be
+        * double the length of the passed array, as it takes two characters to
+        * represent any given byte.
+        * 
+        * @param data
+        *            a byte[] to convert to Hex characters
+        * @return A char[] containing hexadecimal characters
+        */
+       public static char[] encodeHex(byte[] data) {
+               return encodeHex(data, true);
+       }
+
+       /**
+        * Converts an array of bytes into an array of characters representing the
+        * hexadecimal values of each byte in order. The returned array will be
+        * double the length of the passed array, as it takes two characters to
+        * represent any given byte.
+        * 
+        * @param data
+        *            a byte[] to convert to Hex characters
+        * @param toLowerCase
+        *            <code>true</code> converts to lowercase, <code>false</code> to
+        *            uppercase
+        * @return A char[] containing hexadecimal characters
+        * @since 1.4
+        */
+       public static char[] encodeHex(byte[] data, boolean toLowerCase) {
+               return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+       }
+
+       /**
+        * Converts an array of bytes into an array of characters representing the
+        * hexadecimal values of each byte in order. The returned array will be
+        * double the length of the passed array, as it takes two characters to
+        * represent any given byte.
+        * 
+        * @param data
+        *            a byte[] to convert to Hex characters
+        * @param toDigits
+        *            the output alphabet
+        * @return A char[] containing hexadecimal characters
+        * @since 1.4
+        */
+       protected static char[] encodeHex(byte[] data, char[] toDigits) {
+               int l = data.length;
+               char[] out = new char[l << 1];
+               // two characters form the hex value.
+               for (int i = 0, j = 0; i < l; i++) {
+                       out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
+                       out[j++] = toDigits[0x0F & data[i]];
+               }
+               return out;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/CsvParser.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/CsvParser.java
new file mode 100644 (file)
index 0000000..7680b32
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * 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.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.argeo.ArgeoException;
+import org.argeo.StreamUtils;
+
+/**
+ * Parses a CSV file interpreting the first line as a header. The
+ * {@link #parse(InputStream)} method and the setters are synchronized so that
+ * the object cannot be modified when parsing.
+ */
+public abstract class CsvParser {
+       private char separator = ',';
+       private char quote = '\"';
+
+       private Boolean noHeader = false;
+       private Boolean strictLineAsLongAsHeader = true;
+
+       /**
+        * Actually process a parsed line. If
+        * {@link #setStrictLineAsLongAsHeader(Boolean)} is true (default) the
+        * header and the tokens are guaranteed to have the same size.
+        * 
+        * @param lineNumber
+        *            the current line number, starts at 1 (the header, if header
+        *            processing is enabled, the first line otherwise)
+        * @param header
+        *            the read-only header or null if {@link #setNoHeader(Boolean)}
+        *            is true (default is false)
+        * @param tokens
+        *            the parsed tokens
+        */
+       protected abstract void processLine(Integer lineNumber,
+                       List<String> header, List<String> tokens);
+
+       /**
+        * Parses the CSV file (stream is closed at the end)
+        */
+       public synchronized void parse(InputStream in) {
+               parse(in, null);
+       }
+
+       /**
+        * Parses the CSV file (stream is closed at the end)
+        */
+       public synchronized void parse(InputStream in, String encoding) {
+               BufferedReader reader = null;
+               Integer lineCount = 0;
+               try {
+                       if (encoding == null)
+                               reader = new BufferedReader(new InputStreamReader(in));
+                       else
+                               reader = new BufferedReader(new InputStreamReader(in, encoding));
+                       List<String> header = null;
+                       if (!noHeader) {
+                               String headerStr = reader.readLine();
+                               if (headerStr == null)// empty file
+                                       return;
+                               lineCount++;
+                               header = new ArrayList<String>();
+                               StringBuffer currStr = new StringBuffer("");
+                               Boolean wasInquote = false;
+                               while (parseLine(headerStr, header, currStr, wasInquote)) {
+                                       headerStr = reader.readLine();
+                                       if (headerStr == null)
+                                               break;
+                                       wasInquote = true;
+                               }
+                               header = Collections.unmodifiableList(header);
+                       }
+
+                       String line = null;
+                       lines: while ((line = reader.readLine()) != null) {
+                               line = preProcessLine(line);
+                               if (line == null) {
+                                       // skip line
+                                       continue lines;
+                               }
+                               lineCount++;
+                               List<String> tokens = new ArrayList<String>();
+                               StringBuffer currStr = new StringBuffer("");
+                               Boolean wasInquote = false;
+                               sublines: while (parseLine(line, tokens, currStr, wasInquote)) {
+                                       line = reader.readLine();
+                                       if (line == null)
+                                               break sublines;
+                                       wasInquote = true;
+                               }
+                               if (!noHeader && strictLineAsLongAsHeader) {
+                                       int headerSize = header.size();
+                                       int tokenSize = tokens.size();
+                                       if (tokenSize == 1 && line.trim().equals(""))
+                                               continue lines;// empty line
+                                       if (headerSize != tokenSize) {
+                                               throw new ArgeoException("Token size " + tokenSize
+                                                               + " is different from header size "
+                                                               + headerSize + " at line " + lineCount
+                                                               + ", line: " + line + ", header: " + header
+                                                               + ", tokens: " + tokens);
+                                       }
+                               }
+                               processLine(lineCount, header, tokens);
+                       }
+               } catch (ArgeoException e) {
+                       throw e;
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot parse CSV file (line: "
+                                       + lineCount + ")", e);
+               } finally {
+                       StreamUtils.closeQuietly(reader);
+               }
+       }
+
+       /**
+        * Called before each (logical) line is processed, giving a change to modify
+        * it (typically for cleaning dirty files). To be overridden, return the
+        * line unchanged by default. Skip the line if 'null' is returned.
+        */
+       protected String preProcessLine(String line) {
+               return line;
+       }
+
+       /**
+        * Parses a line character by character for performance purpose
+        * 
+        * @return whether to continue parsing this line
+        */
+       protected Boolean parseLine(String str, List<String> tokens,
+                       StringBuffer currStr, Boolean wasInquote) {
+               // List<String> tokens = new ArrayList<String>();
+
+               // System.out.println("#LINE: " + str);
+
+               if (wasInquote)
+                       currStr.append('\n');
+
+               char[] arr = str.toCharArray();
+               boolean inQuote = wasInquote;
+               // StringBuffer currStr = new StringBuffer("");
+               for (int i = 0; i < arr.length; i++) {
+                       char c = arr[i];
+                       if (c == separator) {
+                               if (!inQuote) {
+                                       tokens.add(currStr.toString());
+                                       // System.out.println("# TOKEN: " + currStr);
+                                       currStr.delete(0, currStr.length());
+                               } else {
+                                       // we don't remove separator that are in a quoted substring
+                                       // System.out
+                                       // .println("IN QUOTE, got a separator: [" + c + "]");
+                                       currStr.append(c);
+                               }
+                       } else if (c == quote) {
+                               if (inQuote && (i + 1) < arr.length && arr[i + 1] == quote) {
+                                       // case of double quote
+                                       currStr.append(quote);
+                                       i++;
+                               } else {// standard
+                                       inQuote = inQuote ? false : true;
+                               }
+                       } else {
+                               currStr.append(c);
+                       }
+               }
+
+               if (!inQuote) {
+                       tokens.add(currStr.toString());
+                       // System.out.println("# TOKEN: " + currStr);
+               }
+               // if (inQuote)
+               // throw new ArgeoException("Missing quote at the end of the line "
+               // + str + " (parsed: " + tokens + ")");
+               if (inQuote)
+                       return true;
+               else
+                       return false;
+               // return tokens;
+       }
+
+       public char getSeparator() {
+               return separator;
+       }
+
+       public synchronized void setSeparator(char separator) {
+               this.separator = separator;
+       }
+
+       public char getQuote() {
+               return quote;
+       }
+
+       public synchronized void setQuote(char quote) {
+               this.quote = quote;
+       }
+
+       public Boolean getNoHeader() {
+               return noHeader;
+       }
+
+       public synchronized void setNoHeader(Boolean noHeader) {
+               this.noHeader = noHeader;
+       }
+
+       public Boolean getStrictLineAsLongAsHeader() {
+               return strictLineAsLongAsHeader;
+       }
+
+       public synchronized void setStrictLineAsLongAsHeader(
+                       Boolean strictLineAsLongAsHeader) {
+               this.strictLineAsLongAsHeader = strictLineAsLongAsHeader;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/CsvParserWithLinesAsMap.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/CsvParserWithLinesAsMap.java
new file mode 100644 (file)
index 0000000..e7baabb
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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.util;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.argeo.ArgeoException;
+
+/**
+ * CSV parser allowing to process lines as maps whose keys are the header
+ * fields.
+ */
+public abstract class CsvParserWithLinesAsMap extends CsvParser {
+
+       /**
+        * Actually processes a line.
+        * 
+        * @param lineNumber
+        *            the current line number, starts at 1 (the header, if header
+        *            processing is enabled, the first lien otherwise)
+        * @param line
+        *            the parsed tokens as a map whose keys are the header fields
+        */
+       protected abstract void processLine(Integer lineNumber,
+                       Map<String, String> line);
+
+       protected final void processLine(Integer lineNumber, List<String> header,
+                       List<String> tokens) {
+               if (header == null)
+                       throw new ArgeoException("Only CSV with header is supported");
+               Map<String, String> line = new HashMap<String, String>();
+               for (int i = 0; i < header.size(); i++) {
+                       String key = header.get(i);
+                       String value = null;
+                       if (i < tokens.size())
+                               value = tokens.get(i);
+                       line.put(key, value);
+               }
+               processLine(lineNumber, line);
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/CsvWriter.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/CsvWriter.java
new file mode 100644 (file)
index 0000000..d90f474
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.List;
+
+import org.argeo.ArgeoException;
+
+/** Write in CSV format. */
+public class CsvWriter {
+       private final Writer out;
+
+       private char separator = ',';
+       private char quote = '\"';
+
+       /**
+        * Creates a CSV writer.
+        * 
+        * @param out
+        *            the stream to write to. Caller is responsible for closing it.
+        */
+       public CsvWriter(OutputStream out) {
+               this.out = new OutputStreamWriter(out);
+       }
+
+       /**
+        * Creates a CSV writer.
+        * 
+        * @param out
+        *            the stream to write to. Caller is responsible for closing it.
+        */
+       public CsvWriter(OutputStream out, String encoding) {
+               try {
+                       this.out = new OutputStreamWriter(out, encoding);
+               } catch (UnsupportedEncodingException e) {
+                       throw new ArgeoException("Cannot initialize CSV writer", e);
+               }
+       }
+
+       /**
+        * Write a CSV line. Also used to write a header if needed (this is
+        * transparent for the CSV writer): simply call it first, before writing the
+        * lines.
+        */
+       public void writeLine(List<?> tokens) {
+               try {
+                       Iterator<?> it = tokens.iterator();
+                       while (it.hasNext()) {
+                               writeToken(it.next().toString());
+                               if (it.hasNext())
+                                       out.write(separator);
+                       }
+                       out.write('\n');
+                       out.flush();
+               } catch (IOException e) {
+                       throw new ArgeoException("Could not write " + tokens, e);
+               }
+       }
+
+       /**
+        * Write a CSV line. Also used to write a header if needed (this is
+        * transparent for the CSV writer): simply call it first, before writing the
+        * lines.
+        */
+       public void writeLine(Object[] tokens) {
+               try {
+                       for (int i = 0; i < tokens.length; i++) {
+                               if (tokens[i] == null) {
+                                       // TODO configure how to deal with null
+                                       writeToken("");
+                               } else {
+                                       writeToken(tokens[i].toString());
+                               }
+                               if (i != (tokens.length - 1))
+                                       out.write(separator);
+                       }
+                       out.write('\n');
+                       out.flush();
+               } catch (IOException e) {
+                       throw new ArgeoException("Could not write " + tokens, e);
+               }
+       }
+
+       protected void writeToken(String token) throws IOException {
+               // +2 for possible quotes, another +2 assuming there would be an already
+               // quoted string where quotes needs to be duplicated
+               // another +2 for safety
+               // we don't want to increase buffer size while writing
+               StringBuffer buf = new StringBuffer(token.length() + 6);
+               char[] arr = token.toCharArray();
+               boolean shouldQuote = false;
+               for (char c : arr) {
+                       if (!shouldQuote) {
+                               if (c == separator)
+                                       shouldQuote = true;
+                               if (c == '\n')
+                                       shouldQuote = true;
+                       }
+
+                       if (c == quote) {
+                               shouldQuote = true;
+                               // duplicate quote
+                               buf.append(quote);
+                       }
+
+                       // generic case
+                       buf.append(c);
+               }
+
+               if (shouldQuote == true)
+                       out.write(quote);
+               out.write(buf.toString());
+               if (shouldQuote == true)
+                       out.write(quote);
+       }
+
+       public void setSeparator(char separator) {
+               this.separator = separator;
+       }
+
+       public void setQuote(char quote) {
+               this.quote = quote;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/LocaleCallback.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/LocaleCallback.java
new file mode 100644 (file)
index 0000000..60bf4c4
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import javax.security.auth.callback.Callback;
+
+/** Choose in a list of locales */
+public class LocaleCallback implements Callback {
+       private List<Locale> availableLocales = new ArrayList<Locale>();
+
+       private Integer selectedIndex = null;
+       private Integer defaultIndex = null;
+       private String prompt = "Language";
+
+       public LocaleCallback(Integer defaultIndex, List<Locale> availableLocales) {
+               this.availableLocales = Collections
+                               .unmodifiableList(new ArrayList<Locale>(availableLocales));
+               this.defaultIndex = defaultIndex;
+               this.selectedIndex = defaultIndex;
+       }
+
+       /**
+        * Convenience constructor based on a comma separated list of iso codes (en,
+        * en_US, fr_CA, etc.). Default selection is default locale.
+        */
+       public LocaleCallback(String locales) {
+               if (locales == null || locales.trim().equals(""))
+                       return;
+               String[] codes = locales.split(",");
+               for (int i = 0; i < codes.length; i++) {
+                       String code = codes[i];
+                       // variant not supported
+                       int indexUnd = code.indexOf("_");
+                       Locale locale;
+                       if (indexUnd > 0) {
+                               String language = code.substring(0, indexUnd);
+                               String country = code.substring(indexUnd + 1);
+                               locale = new Locale(language, country);
+                       } else {
+                               locale = new Locale(code);
+                       }
+                       availableLocales.add(locale);
+                       if (locale.equals(Locale.getDefault()))
+                               defaultIndex = i;
+               }
+
+               if (defaultIndex == null)
+                       defaultIndex = 0;
+
+               this.selectedIndex = defaultIndex;
+       }
+
+       public String[] getSupportedLocalesLabels() {
+               String[] labels = new String[availableLocales.size()];
+               for (int i = 0; i < availableLocales.size(); i++) {
+                       Locale locale = availableLocales.get(i);
+                       if (locale.getCountry().equals(""))
+                               labels[i] = locale.getDisplayLanguage(locale) + " ["
+                                               + locale.getLanguage() + "]";
+                       else
+                               labels[i] = locale.getDisplayLanguage(locale) + " ("
+                                               + locale.getDisplayCountry(locale) + ") ["
+                                               + locale.getLanguage() + "_" + locale.getCountry()
+                                               + "]";
+
+               }
+               return labels;
+       }
+
+       public Locale getSelectedLocale() {
+               if (selectedIndex == null)
+                       return null;
+               return availableLocales.get(selectedIndex);
+       }
+
+       public void setSelectedIndex(Integer selectedIndex) {
+               this.selectedIndex = selectedIndex;
+       }
+
+       public Integer getDefaultIndex() {
+               return defaultIndex;
+       }
+
+       public String getPrompt() {
+               // TODO localize it?
+               return prompt;
+       }
+
+       public void setPrompt(String prompt) {
+               this.prompt = prompt;
+       }
+
+       public List<Locale> getAvailableLocales() {
+               return availableLocales;
+       }
+
+       public static void main(String[] args) {
+               for (String isoL : Locale.getISOLanguages()) {
+                       Locale locale = new Locale(isoL);
+                       System.out.println(isoL + "\t" + locale.getDisplayLanguage() + "\t"
+                                       + locale.getDisplayLanguage(locale));
+               }
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/LocaleUtils.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/LocaleUtils.java
new file mode 100644 (file)
index 0000000..bc04afa
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.util;
+
+import java.util.Locale;
+
+import org.argeo.ArgeoException;
+
+/** Utilities around internationalization. */
+public class LocaleUtils {
+       /**
+        * The locale of the current thread and its children. Allows to deal with
+        * internationalisation as a cross cutting aspect. Never null.
+        */
+       public final static InheritableThreadLocal<Locale> threadLocale = new InheritableThreadLocal<Locale>() {
+               @Override
+               protected Locale initialValue() {
+                       return Locale.getDefault();
+               }
+
+               @Override
+               public void set(Locale value) {
+                       if (value == null)
+                               throw new ArgeoException("Thread local cannot be null.");
+                       super.set(value);
+               }
+
+       };
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/Throughput.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/Throughput.java
new file mode 100644 (file)
index 0000000..d081189
--- /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.util;
+
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+import org.argeo.ArgeoException;
+
+public class Throughput {
+       private final static NumberFormat usNumberFormat = NumberFormat
+                       .getInstance(Locale.US);
+
+       public enum Unit {
+               s, m, h, d
+       }
+
+       private final Double value;
+       private final Unit unit;
+
+       public Throughput(Double value, Unit unit) {
+               this.value = value;
+               this.unit = unit;
+       }
+
+       public Throughput(Long periodMs, Long count, Unit unit) {
+               if (unit.equals(Unit.s))
+                       value = ((double) count * 1000d) / periodMs;
+               else if (unit.equals(Unit.m))
+                       value = ((double) count * 60d * 1000d) / periodMs;
+               else if (unit.equals(Unit.h))
+                       value = ((double) count * 60d * 60d * 1000d) / periodMs;
+               else if (unit.equals(Unit.d))
+                       value = ((double) count * 24d * 60d * 60d * 1000d) / periodMs;
+               else
+                       throw new ArgeoException("Unsupported unit " + unit);
+               this.unit = unit;
+       }
+
+       public Throughput(Double value, String unitStr) {
+               this(value, Unit.valueOf(unitStr));
+       }
+
+       public Throughput(String def) {
+               int index = def.indexOf('/');
+               if (def.length() < 3 || index <= 0 || index != def.length() - 2)
+                       throw new ArgeoException(def + " no a proper throughput definition"
+                                       + " (should be <value>/<unit>, e.g. 3.54/s or 1500/h");
+               String valueStr = def.substring(0, index);
+               String unitStr = def.substring(index + 1);
+               try {
+                       this.value = usNumberFormat.parse(valueStr).doubleValue();
+               } catch (ParseException e) {
+                       throw new ArgeoException("Cannot parse " + valueStr
+                                       + " as a number.", e);
+               }
+               this.unit = Unit.valueOf(unitStr);
+       }
+
+       public Long asMsPeriod() {
+               if (unit.equals(Unit.s))
+                       return Math.round(1000d / value);
+               else if (unit.equals(Unit.m))
+                       return Math.round((60d * 1000d) / value);
+               else if (unit.equals(Unit.h))
+                       return Math.round((60d * 60d * 1000d) / value);
+               else if (unit.equals(Unit.d))
+                       return Math.round((24d * 60d * 60d * 1000d) / value);
+               else
+                       throw new ArgeoException("Unsupported unit " + unit);
+       }
+
+       @Override
+       public String toString() {
+               return usNumberFormat.format(value) + '/' + unit;
+       }
+
+       public Double getValue() {
+               return value;
+       }
+
+       public Unit getUnit() {
+               return unit;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/ThroughputEditor.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/ThroughputEditor.java
new file mode 100644 (file)
index 0000000..4ebf26b
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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.util;
+
+import java.beans.PropertyEditorSupport;
+
+public class ThroughputEditor extends PropertyEditorSupport {
+
+       @Override
+       public String getAsText() {
+               return getValue().toString();
+       }
+
+       @Override
+       public void setAsText(String text) throws IllegalArgumentException {
+               setValue(new Throughput(text));
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/security/DigestUtils.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/security/DigestUtils.java
new file mode 100644 (file)
index 0000000..b6aae6f
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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.util.security;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import org.argeo.ArgeoException;
+import org.argeo.StreamUtils;
+
+/** Utilities around cryptographic digests */
+public class DigestUtils {
+       private static Boolean debug = true;
+       // TODO: make it writable
+       private final static Integer byteBufferCapacity = 100 * 1024;// 100 KB
+
+       public static String digest(String algorithm, byte[] bytes) {
+               try {
+                       MessageDigest digest = MessageDigest.getInstance(algorithm);
+                       digest.update(bytes);
+                       byte[] checksum = digest.digest();
+                       String res = StreamUtils.encodeHexString(checksum);
+                       return res;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot digest with algorithm "
+                                       + algorithm, e);
+               }
+       }
+
+       public static String digest(String algorithm, InputStream in) {
+               try {
+                       MessageDigest digest = MessageDigest.getInstance(algorithm);
+                       // ReadableByteChannel channel = Channels.newChannel(in);
+                       // ByteBuffer bb = ByteBuffer.allocateDirect(byteBufferCapacity);
+                       // while (channel.read(bb) > 0)
+                       // digest.update(bb);
+                       byte[] buffer = new byte[byteBufferCapacity];
+                       int read = 0;
+                       while ((read = in.read(buffer)) > 0) {
+                               digest.update(buffer, 0, read);
+                       }
+
+                       byte[] checksum = digest.digest();
+                       String res = StreamUtils.encodeHexString(checksum);
+                       return res;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot digest with algorithm "
+                                       + algorithm, e);
+               } finally {
+                       StreamUtils.closeQuietly(in);
+               }
+       }
+
+       public static String digest(String algorithm, File file) {
+               FileInputStream fis = null;
+               FileChannel fc = null;
+               try {
+                       fis = new FileInputStream(file);
+                       fc = fis.getChannel();
+
+                       // Get the file's size and then map it into memory
+                       int sz = (int) fc.size();
+                       ByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz);
+                       return digest(algorithm, bb);
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot digest " + file
+                                       + " with algorithm " + algorithm, e);
+               } finally {
+                       StreamUtils.closeQuietly(fis);
+                       if (fc.isOpen())
+                               try {
+                                       fc.close();
+                               } catch (IOException e) {
+                                       // silent
+                               }
+               }
+       }
+
+       protected static String digest(String algorithm, ByteBuffer bb) {
+               long begin = System.currentTimeMillis();
+               try {
+                       MessageDigest digest = MessageDigest.getInstance(algorithm);
+                       digest.update(bb);
+                       byte[] checksum = digest.digest();
+                       String res = StreamUtils.encodeHexString(checksum);
+                       long end = System.currentTimeMillis();
+                       if (debug)
+                               System.out.println((end - begin) + " ms / "
+                                               + ((end - begin) / 1000) + " s");
+                       return res;
+               } catch (NoSuchAlgorithmException e) {
+                       throw new ArgeoException("Cannot digest with algorithm "
+                                       + algorithm, e);
+               }
+       }
+
+       public static void main(String[] args) {
+               File file;
+               if (args.length > 0)
+                       file = new File(args[0]);
+               else {
+                       System.err.println("Usage: <file> [<algorithm>]"
+                                       + " (see http://java.sun.com/j2se/1.5.0/"
+                                       + "docs/guide/security/CryptoSpec.html#AppA)");
+                       return;
+               }
+
+               if (args.length > 1) {
+                       String algorithm = args[1];
+                       System.out.println(digest(algorithm, file));
+               } else {
+                       String algorithm = "MD5";
+                       System.out.println(algorithm + ": " + digest(algorithm, file));
+                       algorithm = "SHA";
+                       System.out.println(algorithm + ": " + digest(algorithm, file));
+                       algorithm = "SHA-256";
+                       System.out.println(algorithm + ": " + digest(algorithm, file));
+                       algorithm = "SHA-512";
+                       System.out.println(algorithm + ": " + digest(algorithm, file));
+               }
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/security/Keyring.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/security/Keyring.java
new file mode 100644 (file)
index 0000000..10dbabd
--- /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.util.security;
+
+import java.io.InputStream;
+
+/**
+ * Access to private (typically encrypted) data. The keyring is responsible for
+ * retrieving the necessary credentials. <b>Experimental. This API may
+ * change.</b>
+ */
+public interface Keyring {
+       public void changePassword(char[] oldPassword, char[] newPassword);
+
+       /**
+        * Returns the confidential information as chars. Must ask for it if it is
+        * not stored.
+        */
+       public char[] getAsChars(String path);
+
+       /**
+        * Returns the confidential information as a stream. Must ask for it if it
+        * is not stored.
+        */
+       public InputStream getAsStream(String path);
+
+       public void set(String path, char[] arr);
+
+       public void set(String path, InputStream in);
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/security/SimplePrincipal.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/security/SimplePrincipal.java
new file mode 100644 (file)
index 0000000..c7a4daf
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.util.security;
+
+import java.security.Principal;
+
+import org.argeo.ArgeoException;
+
+/** Canonical implementation of a {@link Principal} */
+public class SimplePrincipal implements Principal {
+       private final String name;
+
+       public SimplePrincipal(String name) {
+               if (name == null)
+                       throw new ArgeoException("Principal name cannot be null");
+               this.name = name;
+       }
+
+       public String getName() {
+               return name;
+       }
+
+       @Override
+       public int hashCode() {
+               return name.hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (obj == null)
+                       return false;
+               return name.equals(obj.toString());
+       }
+
+       @Override
+       protected Object clone() throws CloneNotSupportedException {
+               return new SimplePrincipal(name);
+       }
+
+       @Override
+       public String toString() {
+               return name;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/ArrayTabularRow.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/ArrayTabularRow.java
new file mode 100644 (file)
index 0000000..b1672e9
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.util.tabular;
+
+import java.util.List;
+
+/** Minimal tabular row wrapping an {@link Object} array */
+public class ArrayTabularRow implements TabularRow {
+       private final Object[] arr;
+
+       public ArrayTabularRow(List<?> objs) {
+               this.arr = objs.toArray();
+       }
+
+       public Object get(Integer col) {
+               return arr[col];
+       }
+
+       public int size() {
+               return arr.length;
+       }
+
+       public Object[] toArray() {
+               return arr;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/CsvTabularWriter.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/CsvTabularWriter.java
new file mode 100644 (file)
index 0000000..8f5c52d
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.util.tabular;
+
+import java.io.OutputStream;
+
+import org.argeo.util.CsvWriter;
+
+/** Write tabular content in a stream as CSV. Wraps a {@link CsvWriter}. */
+public class CsvTabularWriter implements TabularWriter {
+       private CsvWriter csvWriter;
+
+       public CsvTabularWriter(OutputStream out) {
+               this.csvWriter = new CsvWriter(out);
+       }
+
+       public void appendRow(Object[] row) {
+               csvWriter.writeLine(row);
+       }
+
+       public void close() {
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularColumn.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularColumn.java
new file mode 100644 (file)
index 0000000..a5ee32f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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.util.tabular;
+
+/** The column in a tabular content */
+public class TabularColumn {
+       private String name;
+       /**
+        * JCR types, see
+        * http://www.day.com/maven/javax.jcr/javadocs/jcr-2.0/index.html
+        * ?javax/jcr/PropertyType.html
+        */
+       private Integer type;
+
+       /** column with default type */
+       public TabularColumn(String name) {
+               super();
+               this.name = name;
+       }
+
+       public TabularColumn(String name, Integer type) {
+               super();
+               this.name = name;
+               this.type = type;
+       }
+
+       public String getName() {
+               return name;
+       }
+
+       public void setName(String name) {
+               this.name = name;
+       }
+
+       public Integer getType() {
+               return type;
+       }
+
+       public void setType(Integer type) {
+               this.type = type;
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularContent.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularContent.java
new file mode 100644 (file)
index 0000000..a2da866
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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.util.tabular;
+
+import java.util.List;
+
+/**
+ * Content organized as a table, possibly with headers. Only JCR types are
+ * supported even though there is not direct dependency on JCR.
+ */
+public interface TabularContent {
+       /** The headers of this table or <code>null</code> is none available. */
+       public List<TabularColumn> getColumns();
+
+       public TabularRowIterator read();
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularRow.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularRow.java
new file mode 100644 (file)
index 0000000..05b8a40
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.util.tabular;
+
+/** A row of tabular data */
+public interface TabularRow {
+       /** The value at this column index */
+       public Object get(Integer col);
+
+       /** The raw objects (direct references) */
+       public Object[] toArray();
+
+       /** Number of columns */
+       public int size();
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularRowIterator.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularRowIterator.java
new file mode 100644 (file)
index 0000000..043463d
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.util.tabular;
+
+import java.util.Iterator;
+
+/** Navigation of rows */
+public interface TabularRowIterator extends Iterator<TabularRow> {
+       /**
+        * Current row number, has to be incremented by each call to next() ; starts at 0, will
+        * therefore be 1 for the first row returned.
+        */
+       public Long getCurrentRowNumber();
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularWriter.java b/trunk/base/runtime/org.argeo.util/src/main/java/org/argeo/util/tabular/TabularWriter.java
new file mode 100644 (file)
index 0000000..20d6519
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.util.tabular;
+
+
+/** Write to a tabular content */
+public interface TabularWriter {
+       /** Append a new row of data */
+       public void appendRow(Object[] row);
+
+       /** Finish persisting data and release resources */
+       public void close();
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvParserEncodingTestCase.java b/trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvParserEncodingTestCase.java
new file mode 100644 (file)
index 0000000..111a873
--- /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.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+public class CsvParserEncodingTestCase extends TestCase {
+
+       private String iso = "ISO-8859-1";
+       private String utf8 = "UTF-8";
+
+       public void testParse() throws Exception {
+
+               String xml = new String("áéíóúñ,éééé");
+               byte[] utfBytes = xml.getBytes(utf8);
+               byte[] isoBytes = xml.getBytes(iso);
+
+               InputStream inUtf = new ByteArrayInputStream(utfBytes);
+               InputStream inIso = new ByteArrayInputStream(isoBytes);
+
+               CsvParser csvParser = new CsvParser() {
+                       protected void processLine(Integer lineNumber, List<String> header,
+                                       List<String> tokens) {
+                               assertEquals(header.size(), tokens.size());
+                               assertEquals(2, tokens.size());
+                               assertEquals("áéíóúñ", tokens.get(0));
+                               assertEquals("éééé", tokens.get(1));
+                       }
+               };
+
+               csvParser.parse(inUtf, utf8);
+               inUtf.close();
+               csvParser.parse(inIso, iso);
+               inIso.close();
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvParserParseFileTest.java b/trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvParserParseFileTest.java
new file mode 100644 (file)
index 0000000..a937ee7
--- /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.util;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class CsvParserParseFileTest extends TestCase {
+       public void testParse() throws Exception {
+
+               final Map<Integer, Map<String, String>> lines = new HashMap<Integer, Map<String, String>>();
+               InputStream in = getClass().getResourceAsStream(
+                               "/org/argeo/util/ReferenceFile.csv");
+               CsvParserWithLinesAsMap parser = new CsvParserWithLinesAsMap() {
+                       protected void processLine(Integer lineNumber,
+                                       Map<String, String> line) {
+                               lines.put(lineNumber, line);
+                       }
+               };
+
+               parser.parse(in);
+               in.close();
+
+               assertEquals(5, lines.size());
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvParserTestCase.java b/trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvParserTestCase.java
new file mode 100644 (file)
index 0000000..02e8d1b
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+public class CsvParserTestCase extends TestCase {
+       public void testParse() throws Exception {
+               String toParse = "Header1,\"Header\n2\",Header3,\"Header4\"\n"
+                               + "Col1,\"Col\n2\",Col3,\"\"\"Col4\"\"\"\n"
+                               + "Col1,\"Col\n2\",Col3,\"\"\"Col4\"\"\"\n"
+                               + "Col1,\"Col\n2\",Col3,\"\"\"Col4\"\"\"\n";
+
+               InputStream in = new ByteArrayInputStream(toParse.getBytes());
+
+               CsvParser csvParser = new CsvParser() {
+                       protected void processLine(Integer lineNumber, List<String> header,
+                                       List<String> tokens) {
+                               assertEquals(header.size(), tokens.size());
+                               assertEquals(4, tokens.size());
+                               assertEquals("Col1", tokens.get(0));
+                               assertEquals("Col\n2", tokens.get(1));
+                               assertEquals("Col3", tokens.get(2));
+                               assertEquals("\"Col4\"", tokens.get(3));
+                       }
+               };
+
+               csvParser.parse(in);
+               in.close();
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvParserWithQuotedSeparatorTest.java b/trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvParserWithQuotedSeparatorTest.java
new file mode 100644 (file)
index 0000000..d4131a0
--- /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.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class CsvParserWithQuotedSeparatorTest extends TestCase {
+       public void testSimpleParse() throws Exception {
+               String toParse = "Header1,\"Header2\",Header3,\"Header4\"\n"
+                               + "\"Col1, Col2\",\"Col\n2\",Col3,\"\"\"Col4\"\"\"\n";
+
+               InputStream in = new ByteArrayInputStream(toParse.getBytes());
+
+               CsvParser csvParser = new CsvParser() {
+                       protected void processLine(Integer lineNumber, List<String> header,
+                                       List<String> tokens) {
+                               assertEquals(header.size(), tokens.size());
+                               assertEquals(4, tokens.size());
+                               assertEquals("Col1, Col2", tokens.get(0));
+                       }
+               };
+               // System.out.println(toParse);
+               csvParser.parse(in);
+               in.close();
+
+       }
+
+       public void testParseFile() throws Exception {
+
+               final Map<Integer, Map<String, String>> lines = new HashMap<Integer, Map<String, String>>();
+               InputStream in = getClass().getResourceAsStream(
+                               "/org/argeo/util/ReferenceFile.csv");
+
+               CsvParserWithLinesAsMap parser = new CsvParserWithLinesAsMap() {
+                       protected void processLine(Integer lineNumber,
+                                       Map<String, String> line) {
+                               // System.out.println("processing line #" + lineNumber);
+                               lines.put(lineNumber, line);
+                       }
+               };
+
+               parser.parse(in);
+               in.close();
+
+               Map<String, String> line = lines.get(2);
+               assertEquals(",,,,", line.get("Coma testing"));
+               line = lines.get(3);
+               assertEquals(",, ,,", line.get("Coma testing"));
+               line = lines.get(4);
+               assertEquals("module1, module2", line.get("Coma testing"));
+               line = lines.get(5);
+               assertEquals("module1,module2", line.get("Coma testing"));
+               line = lines.get(6);
+               assertEquals(",module1,module2, \nmodule3, module4",
+                               line.get("Coma testing"));
+               assertEquals(5, lines.size());
+
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvWriterTestCase.java b/trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/CsvWriterTestCase.java
new file mode 100644 (file)
index 0000000..26b356d
--- /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.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+public class CsvWriterTestCase extends TestCase {
+       public void testWrite() throws Exception {
+               ByteArrayOutputStream out = new ByteArrayOutputStream();
+               final CsvWriter csvWriter = new CsvWriter(out);
+
+               String[] header = { "Header1", "Header 2", "Header,3", "Header\n4",
+                               "Header\"5\"" };
+               String[] line1 = { "Value1", "Value 2", "Value,3", "Value\n4",
+                               "Value\"5\"" };
+               csvWriter.writeLine(Arrays.asList(header));
+               csvWriter.writeLine(Arrays.asList(line1));
+
+               String reference = "Header1,Header 2,\"Header,3\",\"Header\n4\",\"Header\"\"5\"\"\"\n"
+                               + "Value1,Value 2,\"Value,3\",\"Value\n4\",\"Value\"\"5\"\"\"\n";
+               String written = new String(out.toByteArray());
+               assertEquals(reference, written);
+               out.close();
+               System.out.println(written);
+
+               final List<String> allTokens = new ArrayList<String>();
+               CsvParser csvParser = new CsvParser() {
+                       protected void processLine(Integer lineNumber, List<String> header,
+                                       List<String> tokens) {
+                               if (lineNumber == 2)
+                                       allTokens.addAll(header);
+                               allTokens.addAll(tokens);
+                       }
+               };
+               ByteArrayInputStream in = new ByteArrayInputStream(written.getBytes());
+               csvParser.parse(in);
+               in.close();
+               List<String> allTokensRef = new ArrayList<String>();
+               allTokensRef.addAll(Arrays.asList(header));
+               allTokensRef.addAll(Arrays.asList(line1));
+
+               assertEquals(allTokensRef.size(), allTokens.size());
+               for (int i = 0; i < allTokensRef.size(); i++)
+                       assertEquals(allTokensRef.get(i), allTokens.get(i));
+       }
+
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/ThroughputTest.java b/trunk/base/runtime/org.argeo.util/src/test/java/org/argeo/util/ThroughputTest.java
new file mode 100644 (file)
index 0000000..fc8007e
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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.util;
+
+import junit.framework.TestCase;
+
+public class ThroughputTest extends TestCase {
+       public void testParse() {
+               Throughput t;
+               t = new Throughput("3.54/s");
+               assertEquals(3.54d, t.getValue());
+               assertEquals(Throughput.Unit.s, t.getUnit());
+               assertEquals(282l, (long) t.asMsPeriod());
+
+               t = new Throughput("35698.2569/h");
+               assertEquals(Throughput.Unit.h, t.getUnit());
+               assertEquals(101l, (long) t.asMsPeriod());
+       }
+}
diff --git a/trunk/base/runtime/org.argeo.util/src/test/resources/org/argeo/util/ReferenceFile.csv b/trunk/base/runtime/org.argeo.util/src/test/resources/org/argeo/util/ReferenceFile.csv
new file mode 100644 (file)
index 0000000..351453d
--- /dev/null
@@ -0,0 +1,37 @@
+"ID","A long Text","Name","Other","Number","Reference","Target","Date","Update","Language","ID Ref","Weird chars","line feeds","after line feed","Empty column","Status comment","Comments","Empty","Coma testing"
+"AK251","Everything & with some line feed 
+ more “some” quote","Marge S.",,78.6,"A1155222221111268515131",,12/12/12,03/12/08,,9821308500721,"%%%ùù","ao","Nothing special",,,"Some very usefull comment",,",,,,"
+"AG254","same","Roger “wallace” Big","15 – JI",78.5,"A1155222221111268515131","next milestone",12/12/12,03/12/08,"_fr (French - France)",9812309500953,"***µ”","a
+
+
+
+
+o","after line feed",,"Do the job",,,",, ,,"
+"FG211","Very long text with some bullets.
+1 first
+2 second
+3. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long","Father & Son","15 – JI",15.4,"A1155222221111268515131","next milestone",12/12/12,03/12/08,"_fr (French - France)",9812309500952,"///","a
+
+
+
+
+
+
+o","module1,module2",,"Be fast",,,"module1, module2"
+"RRT152","Very long text with some bullets.
+1 first
+2 second
+3. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long. some more very very very long","Another $$","15 – JI",12.3,"A1155222221111268515131","next milestone",12/12/12,03/12/08,"_fr (French - France)",9812309500950,"---","a
+
+o
+
+
+","module1,module2",,,,,"module1,module2"
+"YU121","Another use case : “blank line”
+
+After the blank.","nothing with brackets( )","15 – JI",15.2,"A1155222221111268515131",,12/12/12,03/12/08,"_fr (French - France)",9812309500925,",;:?./","ao","
+
+
+
+After line feed again",,,,,",module1,module2, 
+module3, module4"
diff --git a/trunk/base/runtime/org.argeo.util/src/test/resources/org/argeo/util/TestParse-ISO.csv b/trunk/base/runtime/org.argeo.util/src/test/resources/org/argeo/util/TestParse-ISO.csv
new file mode 100644 (file)
index 0000000..0bec611
--- /dev/null
@@ -0,0 +1,8 @@
+"Date d'imputation","N° de compte","Code journal","Pièce interne","Pièce externe","Libellé d'écriture","Débit","Crédit","Lettrage","Quantité","Code analytique","Date d'échéance","Date d'imputation origine","Code journal origine","Mode de règlement","Date début de période","Date fin de période"
+26.01.2010,"101300","BQ","BQ01.10",,"Depot société en formation",,"3.000,00",,,,"          ",26.01.2010,"BQ","    ","          ","          "
+26.01.2010,"101300","BQ","BQ01.10",,"Depot société en formation",,"7.000,00",,,,"          ",26.01.2010,"BQ","    ","          ","          "
+26.01.2010,"411OPEN","BQ","BQ01.10",,"Vir Client ",,"2.508,00","A",,,"          ",26.01.2010,"BQ","    ","          ","          "
+26.01.2010,"455100","BQ","BQ01.10",,"Bankomat Raiffeise","250,00",,,,,"          ",26.01.2010,"BQ","    ","          ","          "
+26.01.2010,"512101","BQ","BQ01.10",,"Extrait bancaire 01.10","12.250,55",,,,,"          ",26.01.2010,"BQ","    ","          ","          "
+26.01.2010,"627800","BQ","BQ01.10",,"Envoi de chequier","2,30",,,,,"          ",26.01.2010,"BQ","    ","          ","          "
+26.01.2010,"627800","BQ","BQ01.10",,"Frais d'expedition","5,15",,,,,"          ",26.01.2010,"BQ","    ","          ","          "
diff --git a/trunk/base/runtime/org.argeo.util/src/test/resources/org/argeo/util/TestParse-UTF-8.csv b/trunk/base/runtime/org.argeo.util/src/test/resources/org/argeo/util/TestParse-UTF-8.csv
new file mode 100644 (file)
index 0000000..0bec611
--- /dev/null
@@ -0,0 +1,8 @@
+"Date d'imputation","N° de compte","Code journal","Pièce interne","Pièce externe","Libellé d'écriture","Débit","Crédit","Lettrage","Quantité","Code analytique","Date d'échéance","Date d'imputation origine","Code journal origine","Mode de règlement","Date début de période","Date fin de période"
+26.01.2010,"101300","BQ","BQ01.10",,"Depot société en formation",,"3.000,00",,,,"          ",26.01.2010,"BQ","    ","          ","          "
+26.01.2010,"101300","BQ","BQ01.10",,"Depot société en formation",,"7.000,00",,,,"          ",26.01.2010,"BQ","    ","          ","          "
+26.01.2010,"411OPEN","BQ","BQ01.10",,"Vir Client ",,"2.508,00","A",,,"          ",26.01.2010,"BQ","    ","          ","          "
+26.01.2010,"455100","BQ","BQ01.10",,"Bankomat Raiffeise","250,00",,,,,"          ",26.01.2010,"BQ","    ","          ","          "
+26.01.2010,"512101","BQ","BQ01.10",,"Extrait bancaire 01.10","12.250,55",,,,,"          ",26.01.2010,"BQ","    ","          ","          "
+26.01.2010,"627800","BQ","BQ01.10",,"Envoi de chequier","2,30",,,,,"          ",26.01.2010,"BQ","    ","          ","          "
+26.01.2010,"627800","BQ","BQ01.10",,"Frais d'expedition","5,15",,,,,"          ",26.01.2010,"BQ","    ","          ","          "
diff --git a/trunk/base/runtime/pom.xml b/trunk/base/runtime/pom.xml
new file mode 100644 (file)
index 0000000..e59a2b4
--- /dev/null
@@ -0,0 +1,22 @@
+<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>base</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <groupId>org.argeo.commons.base</groupId>
+       <artifactId>runtime</artifactId>
+       <name>Commons Base Runtime</name>
+       <packaging>pom</packaging>
+       <modules>
+               <module>org.argeo.util</module>
+               <module>org.argeo.support.junit</module>
+               <module>org.argeo.osgi.boot</module>
+               <module>org.argeo.eclipse.ui</module>
+               <module>org.argeo.eclipse.ui.rap</module>
+               <module>org.argeo.eclipse.ui.rcp</module>
+               <module>org.argeo.eclipse.ui.jcr</module>
+       </modules>
+</project>
diff --git a/trunk/demo/.project b/trunk/demo/.project
new file mode 100644 (file)
index 0000000..d22f4c5
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.commons.demo</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+       </buildSpec>
+       <natures>
+       </natures>
+</projectDescription>
diff --git a/trunk/demo/argeo_node_cli.properties b/trunk/demo/argeo_node_cli.properties
new file mode 100644 (file)
index 0000000..1c33311
--- /dev/null
@@ -0,0 +1,8 @@
+argeo.osgi.start.1.node=\
+org.springframework.osgi.extender,\
+
+argeo.osgi.start.3.node=\
+org.argeo.node.repo.jackrabbit,\
+org.argeo.security.dao.cli,\
+
+log4j.configuration=file:../../log4j.properties
diff --git a/trunk/demo/argeo_node_rcp.properties b/trunk/demo/argeo_node_rcp.properties
new file mode 100644 (file)
index 0000000..a2e7105
--- /dev/null
@@ -0,0 +1,16 @@
+argeo.osgi.start=\
+org.springframework.osgi.extender,\
+org.argeo.node.repo.jackrabbit,\
+org.argeo.security.dao.os,\
+
+#org.argeo.server.catalina.start,\
+#org.springframework.osgi.web.extender,\
+#org.argeo.jackrabbit.webapp,\
+
+log4j.configuration=file:../../log4j.properties
+org.argeo.security.ui.initialPerspective=org.argeo.jcr.ui.explorer.perspective
+eclipse.application=org.argeo.security.ui.rcp.secureUi
+
+#argeo.node.repo.configuration=classpath:/org/argeo/jackrabbit/repository-fs.xml
+#argeo.node.repo.configuration=classpath:/org/argeo/jackrabbit/repository-memory.xml
+#argeo.node.repo.configuration=osgibundle:repository-h2.xml
diff --git a/trunk/demo/argeo_node_rcp_remote.properties b/trunk/demo/argeo_node_rcp_remote.properties
new file mode 100644 (file)
index 0000000..1a6dfba
--- /dev/null
@@ -0,0 +1,12 @@
+argeo.osgi.start=\
+org.springframework.osgi.extender,\
+org.argeo.node.repofactory.jackrabbit,\
+org.argeo.security.dao.jackrabbit,\
+org.argeo.security.equinox,\
+
+#org.argeo.security.ui.initialPerspective=org.argeo.osgi.ui.explorer.perspective
+argeo.node.repo.uri=http://localhost:7070/data/jcr/node
+
+log4j.configuration=file:../../log4j.properties
+
+eclipse.application=org.argeo.security.ui.rcp.secureUi
diff --git a/trunk/demo/argeo_node_web.properties b/trunk/demo/argeo_node_web.properties
new file mode 100644 (file)
index 0000000..65d63bc
--- /dev/null
@@ -0,0 +1,106 @@
+argeo.osgi.start.1.node=\
+org.springframework.osgi.extender,\
+org.argeo.server.ads.server,\
+
+argeo.osgi.start.3.node=\
+org.argeo.node.repo.jackrabbit,\
+org.argeo.security.dao.ldap,\
+
+#argeo.osgi.start.4.node=\
+#org.argeo.jackrabbit.webapp,\
+#org.argeo.server.rap.webapp,\
+#org.argeo.server.catalina.start,\
+#org.eclipse.equinox.http.registry,\
+#org.springframework.osgi.web.extender,\
+
+#argeo.osgi.start.4.node.jetty=\
+#org.eclipse.jetty.server,\
+#org.eclipse.equinox.http.jetty,\
+#org.eclipse.equinox.http.registry,\
+#org.eclipse.equinox.http.servlet,\
+#org.eclipse.equinox.ds,\
+#org.eclipse.osgi.services,\
+#org.eclipse.equinox.launcher,\
+#org.eclipse.rap.ui.workbench,\
+
+#org.eclipse.rap.rwt,\
+#org.eclipse.rap.rwt.osgi,\
+#org.eclipse.equinox.ds,\
+
+argeo.osgi.start.4.node.rap=\
+org.eclipse.osgi,\
+org.eclipse.osgi.services,\
+org.eclipse.equinox.console,\
+org.apache.felix.gogo.command,\
+org.apache.felix.gogo.shell,\
+org.apache.felix.gogo.runtime,\
+org.eclipse.equinox.http.jetty,\
+org.eclipse.equinox.http.servlet,\
+org.eclipse.equinox.util,\
+org.eclipse.jetty.continuation,\
+org.eclipse.jetty.http,\
+org.eclipse.jetty.io,\
+org.eclipse.jetty.security,\
+org.eclipse.jetty.server,\
+org.eclipse.jetty.servlet,\
+org.eclipse.jetty.util,\
+javax.servlet,\
+org.eclipse.rap.jface,\
+org.eclipse.rap.jface.databinding,\
+org.eclipse.rap.ui,\
+org.eclipse.rap.ui.forms,\
+org.eclipse.rap.ui.views,\
+org.eclipse.rap.ui.workbench,\
+com.ibm.icu.base,\
+org.eclipse.core.commands,\
+org.eclipse.core.contenttype,\
+org.eclipse.core.databinding,\
+org.eclipse.core.databinding.beans,\
+org.eclipse.core.databinding.observable,\
+org.eclipse.core.databinding.property,\
+org.eclipse.core.expressions,\
+org.eclipse.core.jobs,\
+org.eclipse.core.runtime,\
+org.eclipse.equinox.app,\
+org.eclipse.equinox.common,\
+org.eclipse.equinox.registry,\
+org.eclipse.equinox.preferences,\
+org.eclipse.equinox.http.registry,\
+
+argeo.osgi.start.5.node.rap=\
+org.eclipse.rap.rwt.osgi,\
+
+#org.argeo.server.catalina.start,\
+#org.eclipse.jetty.server,\
+
+# Jetty
+org.osgi.service.http.port=7070
+org.eclipse.equinox.http.jetty.log.stderr.threshold=info
+org.eclipse.equinox.http.jetty.context.path=/ui
+
+# Initial perspective
+#org.argeo.security.ui.initialPerspective=org.argeo.osgi.ui.explorer.perspective
+#org.argeo.security.ui.initialPerspective=org.argeo.jcr.ui.explorer.perspective
+
+log4j.configuration=file:../../log4j.properties
+argeo.server.tomcat.config=conf/default-server-ssl.xml
+
+argeo.i18n.availableLocales=en,fr,de,ru,ar
+eclipse.registry.MultiLanguage=true
+
+osgi.noShutdown=true
+eclipse.ignoreApp=true
+
+# Tomcat
+#argeo.server.port.http=7070
+#argeo.server.port.https=7443
+#argeo.server.port.ajp=7009
+
+# Apache Directory Server
+#argeo.ldap.port=10389
+
+# Equinox (not active by default)
+#osgi.console=3030
+
+# Note default URL to access the webapp
+# http://localhost:7070/ui/node
\ No newline at end of file
diff --git a/trunk/demo/log4j.properties b/trunk/demo/log4j.properties
new file mode 100644 (file)
index 0000000..13f949f
--- /dev/null
@@ -0,0 +1,32 @@
+log4j.rootLogger=WARN, development
+
+## Levels
+log4j.logger.org.argeo=DEBUG
+log4j.logger.org.argeo.jackrabbit.remote.ExtendedDispatcherServlet=WARN
+log4j.logger.org.argeo.server.webextender.TomcatDeployer=INFO
+
+#log4j.logger.org.springframework.security=DEBUG
+#log4j.logger.org.apache.commons.exec=DEBUG
+#log4j.logger.org.apache.jackrabbit.webdav=DEBUG
+#log4j.logger.org.apache.jackrabbit.remote=DEBUG
+#log4j.logger.org.apache.jackrabbit.core.observation=DEBUG
+
+log4j.logger.org.apache.catalina=INFO
+log4j.logger.org.apache.coyote=INFO
+
+log4j.logger.org.apache.directory=INFO
+log4j.logger.org.apache.directory.server=ERROR
+log4j.logger.org.apache.jackrabbit.core.query.lucene=ERROR
+
+## 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 - [%t]%n
+
+# development appender (slow!)
+log4j.appender.development=org.apache.log4j.ConsoleAppender
+log4j.appender.development.layout=org.apache.log4j.PatternLayout
+log4j.appender.development.layout.ConversionPattern=%d{HH:mm:ss} [%16.16t] %5p %m (%F:%L) %c%n
diff --git a/trunk/demo/ssl/ca.crt b/trunk/demo/ssl/ca.crt
new file mode 100644 (file)
index 0000000..1aef418
--- /dev/null
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF3zCCA8egAwIBAgIJALKBUni09meLMA0GCSqGSIb3DQEBBQUAMIGFMQswCQYD
+VQQGEwJERTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xFTATBgNV
+BAoMDEV4YW1wbGUgR21iSDEMMAoGA1UECwwDUiZEMRAwDgYDVQQDDAdEZW1vIENB
+MR0wGwYJKoZIhvcNAQkBFg5jYUBleGFtcGxlLm9yZzAeFw0xMzA1MjgxMTI0MTNa
+Fw0yMzA1MjYxMTI0MTNaMIGFMQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmVybGlu
+MQ8wDQYDVQQHDAZCZXJsaW4xFTATBgNVBAoMDEV4YW1wbGUgR21iSDEMMAoGA1UE
+CwwDUiZEMRAwDgYDVQQDDAdEZW1vIENBMR0wGwYJKoZIhvcNAQkBFg5jYUBleGFt
+cGxlLm9yZzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKwB6jOoHk+e
+LjI8AqqR9VHG53KgQFwjVkTlMJ5QHgUgFFzZB7af9B0AtzbLPSjJH3rY/8itYQnB
+jL1D9Ijc0EAJOrDgtQjYV1jpL49mFI9mgZkCXJv8rehxm8IDIuyPgceW4/ZrogOB
+MJwmWAK8DfDQzuomw/mKhPvssFFq7zW9l1ae2kSniZ+m7pTqESuZ8gB+qvEi/bMC
+nWD4jp+Sr3e1FGlXJu7Ltc1Z+OWmFRa4VlxBF5wgsNVcL4JRx1Wwhu0I5qWxrpZ3
+KdqBiPivM0N8Aaszf2APsatE6BDYdYJsM4KGJ8aInZYjN8hnzmDSui7taI2/vjrx
+0nE2lhxpykyaUxKhXOnfPHxUOCc6XlfZkKA/fg9ZYz3ybqIjL8SU7DOHEjIT7xdc
+WA9ccSCrr8B6Lfk7P5ZM/zdLir59dyogAMJtHiot4Tn44lXe+2RXBDytGTbWNNlM
+Llxv7O0f5b3QUWQ1lybNDRNJBwTIBoJMW6v09S3zf65XyQ3/hrAnXxQ5T4/fqEBP
+15C4g6n6ANg5jCXqYPdnMzR//vDxJZzKQc01BVdKLsSb02yIb2LizOy0ezx9dEZa
+PexiztKD5dwnrzVxHu4DilBcJBTFOkgbMfDau7fLVuF0XXFTc/FSdsaJDDWI5N7N
+8vzHZGZZfm5qasrZryop2DwzSWfCDYTrAgMBAAGjUDBOMB0GA1UdDgQWBBRsDHcK
+aom/nG0ov6N0soe7SHn1kDAfBgNVHSMEGDAWgBRsDHcKaom/nG0ov6N0soe7SHn1
+kDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQAw1gp89iBwBSMEnwcc
+OQ6jJ4BEnrvwY31VJmbA/2EOhLoUyGr4hTYo3RYGQB0OeMfu7B/YkurVVkEcuR9q
+uwV3eRpPUTcGSsisEwvUbu9Iur9ygA7S9+IKZJ4KCS6ZDKpdHO2lOcLwtguky6W2
+PJEJLrKOmXqLQ2epjeHqGe9FXHyk3AY4YtXpZ6Mu/sYTiG+LZscXYFkvIM/MwBC8
+THX8J3dE/v1gPgFHaavW5vhsEI1izPj9KN7wyT7ljJgvnWA5Vbs+w1qW6fCtmTxl
+nY/DvhsVoOj+Zg2iMGByZusm6oP+vVVK6ZCst2RBpoZ059RRY71woRYjXa88LxLM
+rOuzcBxLC+X4o4PJwIVTVKnga2TpxyREjFqJehf8+N9aSuvEM+sY9l1ELQ99zk5v
+juX5kykKI+j28TNtwyDsJNWmzK7OFQAhKMBZZYVLj6Wm6roU7TZ93tLQC1uLRZ82
+DIRDebUbKhGHlqmcQFblUztDzqmCHYvEE0t2mjQNoLDwMzi1d5oSYL0NH5gtuosH
+wDCK0GbR0elYFBcxtX6kceyFnQqOGE5b52rB75OHxJ7iQtCn6Lt3ms9h9ODJjS2e
+Kafzg+RuwOpmIwUOGW140WxNO89e9DCwhR3Q4waRTwzmdLLNEhHhi5GNRHlT7Cem
+gZlju4Nu4PKD2RWyBihI4YF1AA==
+-----END CERTIFICATE-----
diff --git a/trunk/demo/ssl/ca.key b/trunk/demo/ssl/ca.key
new file mode 100644 (file)
index 0000000..2effee0
--- /dev/null
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,1E365E4A0FD2EC89
+
+7MkG03lkphw7WxvG8mc86m6oBdxVibv06X9AbUAZ4CWavS3mO13NYU5K1d4bY4JM
+RKoN3QRJSBaBFy6kCGOEpKnpvKN4fSf/AVYZ7PFbe/oEsi9GruY3sjz2LNc3NgvI
+pPsC1uSWq8vODABzBlh6SKBdznJx403PRPzhR2qXZ6P3893J7qlfgHa7SY7hvY9G
+U4cYyas0Tu67Ta0kxGx87oompsfb/L49iEfEZuuNQnT8rNloiTa3kQRSDiUsVek9
+gq0atye+qgeYRWj054FCV9BC8+cxwYam+f7Fa4Au89Ir8fwFbgihbtzWXoowghwW
+9QTCNqeFu5jb0PYJ9NNY/nWa3lWEve45WZSWgJyDSiYZs/Ei497meXaLiPqtubrv
+aCR1OFDRu++8zElUZsxWdAKohBCfqbkQYe5HfFeenAfX9QZoo+mf8q9Ebj8ojfWl
+wIY+8CyMxla0reG4afsCe4IHfQ7I7TJOBlBMFbMUVBYToWPAK10wIOu5S5v7/gGT
+LauiVPbXVSjZB1wiURBGYqxIcqLktPtAV36XSIGu5geRvb4rT73G+D573jO8QVHA
+Fqw5PKLFA0gPaYjO/D9toPQECut61eHpRw5y4av+H2f/guZAE3UMNcQceYphmhFy
+YxxVBsfoQIVHcE51loEPeWdRvxZWBPvaAP+CFuLDkxzSJ3l1Rjl1kxsc4LOLrKJA
++qzEGMb30T5sS78xo+WTQVXhQOkM+aVVuvZpBZQg2XacTS51Pl/5rpd8m+puTSFO
+Zky4lzpkJwofEwaQeiQxoHnIvniW4Xa0q+ufRPQA6QX8Od1uabS0nuyNS88ezt9d
+yo7kfI6uDxYw1dyqHHsKD/71r3w8oqdtJHzvewK4GMQJqbn7o+i0vVEZ5qL+ZaKl
+/eIFsjxQcqvd5/wGJOyU2jTIotFCHc6AqIlq9sXgRN7F0DpvJCMpdaPpixD46BF8
+3zfRanM6Hx4Mdx9V4zPjujLdNuHMSRR0OzhZsFXkpxUIGE0EnsjrTjzJW8A6dv0P
+LTt3XSe2m8Wkrs7bFCdm4ynZ1wf2ivcB1Np52jP3HUPq9Jeu/VcTY2KnJm0GRsui
+arblNpSqqqi0YSH9KL0VyFFfon2R9u55ZdetPtBUm4PBv3sGqgXwg+a3VqK0CENx
+u//0lI29tIJGzPCMiO5cb9pwsqNbdngeXx7EMfw+bFHb9VVEjctk2Cr4QFKH2+Uf
+c0OvDRdwQdly9zW8Zr8CzSwTLwrIjRoIbNwqhsoTwPN4EXZsicG5hv19sQ1aXs/5
+2IPm968iyqF/ZHAYBNNARK4Pqs+ifMIiCEMpv0ZIovUaHX4/iJZW+UMrq4BqwtPk
+rkI1ZxxbupGFqJEABW8OsZFE8tC5x4lvCntt4tPzQDZuvR3PGcmY+4M9yXkFeYZg
+ol/o6rybVoNRVB/qLjKRWkZNYZUdTp/4zsnBorLaMlIWpyCgTGPQs25Y/7OJI1g5
+l4sZitaW1ucWgsUquWIpqZZCFLZbDGPqKRCZh8k49a7DqEUNf9w+bN6pktRdQ1YR
+uCokQEwUbSZqr4lsBGyJCSfxdZ+A4RdiH5iot6mltUiuKxqYmLfctm002y8daWXh
+Scl9Gv8QtaGu31St67p8FDF6WAZcNXwaP93pC5I2owA17kEbSC65IbNrnU23xqHU
+lEY1hnQuTeiQEXuYIcHURFBLKisKBL4Z1DiSubCK8w3Y9n3LGdMm6lPprLxur7jj
+zvJvWK4ksV+bBzYfhaQ15YVB8R+0DTyGY6LbKtdJ72ZIJsR4zoB5Mtli9xYrdpuA
+SZeE2CSUDRRzaMjdf43gfAM8yAlhQSE2LaQwpGTVrIYLtTJSEVdAk9JYZ+Z8d9DQ
+YeHSm5XROyrqyfnCc50d9Tasaf+ZEzMsmCFnSKalG9g3B4i57TofB6jUyBO8Qob1
+6dDILeXHX4oQ++2HxYeJIMcakiiX/V3rBFJrb0O7swA41cyxu++1x9KYkozHlnkO
+XSspOx3JPadTkcl6BiXpemcqgyd5Xqzx7lit/Y/3lz5mlJr/EIv/qpYpUcFKp2gT
+aijMtflayKignnE8c+1ENq4E8qt8WEuvs8gZG+qu4G8lhaS4dLQNj57W9BeZyAYi
+LvzfFYEVLwUt7mv1zqHLXAGBdvLCZl/DjloXu9RvhF9zOgIR/uNEMFUSyF8v+xZE
+lAwiii2BARCqO+XLo2KkRPsL3fFIItxwEVbpqfTngbe+o/SF/HCXsW+S09y8LNDp
+3Bx2cNIp9+HrDwtRfnN4okjLURxTvki3l19m3rDnvtUgM9AhsA2nNKpEEWAaC5bB
+nPEetLDK7KVniccPro3oI08OwlMGZLM38NgDK66AhgSjkgKri20Wxq5LgG8qLyAE
+kA+g7jjvpaALgbU6PL8k0eLRBldl8NNbdLywZmFOeA27sWNBfblaAeo6MveIGoLY
+Openi1D6uYfLaP/xI0D1Yldmvjgex/P9SDAcRoaj9x5Jaty9oMdGSeSQ+TXA3hrf
+sBsgpn+WN1LR7qBtq+/NdRyu7EXzlgGwpGM3aDUnsFt7iKThwHxEBpyRKXvjL2KO
+jiRaDO9NRpZqj/M0czpGeSXoHNRI/qrqquTDysvYG84rNgxQpWYgYo9R455HPrlQ
+BLlJzr0Nr8H3dp4TDFV0Awpld02FA4XfQ3PUoAnr6ku7CA79GS4PMd2nr4p48GC0
+owtVpm/Bqkp2H1FsnYuBw9FCGEcCe/DTw880V5NiLonUB1L+MVeUhWr/ucJ8txJx
+iBUlg/mxSgfXY715NUgy08UEsTvPw7Ky080RxmEOCIWjxFEvAHE7dCJokHbL8jSO
+8tNlyVPQt9ccEzJbPdag8eMKkIsGIMh1F7HwyLNOrIuM2Cuz9ALTIKwEZ0CE+XBE
+oL5AOn1Cqlvh0fAWUQNP1/RAsT8XqdBXkui/+kvqeVNbZoGJg+wVrNm0sP1nDRqZ
+6oMdcE7tC20YyFl8rkcv2JG+5pJrNQKMosdtQmA2pJ/hS4yZss9vBizJhQHRI4l9
+I7nCHOrxCQvBoxyJb8qKO1cpynN5tttZ0s/njhYXVNtQDpoB2iWFm0/biP63jh4j
+DcqOsuOqMGFtsWPfHM+oMBQ2YT63g4hilMzzilQqUsJ8mBGiYFIcP9CRPUFRI2tQ
+-----END RSA PRIVATE KEY-----
diff --git a/trunk/demo/ssl/demo@demo.crt b/trunk/demo/ssl/demo@demo.crt
new file mode 100644 (file)
index 0000000..d9f0104
--- /dev/null
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFfzCCA2cCAQQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkRFMQ8wDQYD
+VQQIDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjEVMBMGA1UECgwMRXhhbXBsZSBH
+bWJIMQwwCgYDVQQLDANSJkQxEDAOBgNVBAMMB0RlbW8gQ0ExHTAbBgkqhkiG9w0B
+CQEWDmNhQGV4YW1wbGUub3JnMB4XDTEzMDUyODExMzAwNFoXDTIzMDUyNjExMzAw
+NFowgYQxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl
+cmxpbjEVMBMGA1UECgwMRXhhbXBsZSBHbWJIMQwwCgYDVQQLDANSJkQxDTALBgNV
+BAMMBGRlbW8xHzAdBgkqhkiG9w0BCQEWEGRlbW9AZXhhbXBsZS5vcmcwggIiMA0G
+CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDBuEqxINyrCP2IauWReizNWl6J+WgV
+pqTAViqlEtbSbGCzvHE4eF07uCazWmAwWRmXxwf2h4M3siC8Q382n7ri/eS4mijk
+kw7FxoQh1W/jLDFcZ2orY4bz+DoexL+ClVAf8AKBQ68NNbf1aZnUctnDx5Ymrzq/
+KHvgUAIsRmcRzhE5mjWxem33iAVibrQ6LhY5tNDw+VfXZ0IzvTl/ZHfKyOGq4NTl
+21C0iWqksj6sJtvYv88wSHmnzokOXb5rJo5q07HN5b5eHuoAM74SS36ScXpQixDS
+Z4tCjxzo/QwRqQdlWJBv9PSPEq4HNHlCu/zbWZNCbRVKmRf0qYq9dxjxunyj+HjT
+Dj4OfvpmOhTrX78x9ZDOfWmYV/PpoyQ+5PKDhXfFYRGAY97CN1UlQhccdnJCgM3o
+bYkQ+fDXXvgNQLazbOVmeRd5is4TXdcNJIN3LcXXdEOa+O6X8fVTwxzXMD8Uy7fo
+CRvhCrF6gqZZyt+pIJMucLzydiCJHqqDK0xjJ4wtfJNGEyIDJ82hUDEae9X4ao6m
+ti146PH1zbKWS2EYaoEFeXgIqDiU8dKIZNrTd+nflIGd/TLf3LgY43qGEwd/oWtd
+I1hICu173WGdPNnZWbvJtqb4nHag2ldd+jnYHFFF0jnH+neRq4lqW+CAFjWz7c8J
+NtCr+RBVS6bYjwIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQAO6FbFXoYrM1OvUHdF
+mul7cP7jVys3dRNfIdTcKF9tYf5sWS7/2PbFeQ+bjLGxQJcZhQ8BvK2J00/+kExo
+JMEFqQ5BZRorwdnrQxJNJcHrvrQ6ZTaiBhuyktbWGblCAWkuhqw6NLYxhcE1Wq6s
+6w978F/qHCvqGSi1QkVX+9WjVx48K2JqBRKR04THWlgf+llOJsuMv4Rk75iH385L
+FNsQAXIquQHqgBDqKjcSAR0VAQSXV0gnMadaUrHv7H+QqsYo0zp19cX/WF0HNti6
+GHk+mCnDeZh6Z2z7xii+9fQGIMgQ8YhAJwzOhbk72y8WMqfHtFqcY1KnZq52c7wz
+3+kMx+XJ6t6YiJnWAWs4M/mk7RVPOvNqOAh8y+pnd6tZEzdRcvuDiv+U/HwNS6TS
+nEsUHK0rwWD3Sjfwe9LO9TMxdSZkWePlY2v5oAL4YxJnJGfbeBo/OynUkIa0fRXf
+FtKdAENfpijpuNiN3O92q8FwXpLjr38aTHy0o5n2Zrlly7ydGwRXJ8P7FdbGRXj5
+UPaN6b808kysE9zS6BA0XMslLPqQrFgqZFK2fnv/QW87jyXQkTAAgTVrHODJXVsr
+YLIpWTmykOxyNFGYiT9BCxHXTO7LvlGdK3OqRwbzTcD+CHKZsUxALg/q7FbSBS6i
+OZrgJYlxzdsI061+rLsaW02FOA==
+-----END CERTIFICATE-----
diff --git a/trunk/demo/ssl/demo@demo.csr b/trunk/demo/ssl/demo@demo.csr
new file mode 100644 (file)
index 0000000..4ace678
--- /dev/null
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIEyjCCArICAQAwgYQxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzAN
+BgNVBAcMBkJlcmxpbjEVMBMGA1UECgwMRXhhbXBsZSBHbWJIMQwwCgYDVQQLDANS
+JkQxDTALBgNVBAMMBGRlbW8xHzAdBgkqhkiG9w0BCQEWEGRlbW9AZXhhbXBsZS5v
+cmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDBuEqxINyrCP2IauWR
+eizNWl6J+WgVpqTAViqlEtbSbGCzvHE4eF07uCazWmAwWRmXxwf2h4M3siC8Q382
+n7ri/eS4mijkkw7FxoQh1W/jLDFcZ2orY4bz+DoexL+ClVAf8AKBQ68NNbf1aZnU
+ctnDx5Ymrzq/KHvgUAIsRmcRzhE5mjWxem33iAVibrQ6LhY5tNDw+VfXZ0IzvTl/
+ZHfKyOGq4NTl21C0iWqksj6sJtvYv88wSHmnzokOXb5rJo5q07HN5b5eHuoAM74S
+S36ScXpQixDSZ4tCjxzo/QwRqQdlWJBv9PSPEq4HNHlCu/zbWZNCbRVKmRf0qYq9
+dxjxunyj+HjTDj4OfvpmOhTrX78x9ZDOfWmYV/PpoyQ+5PKDhXfFYRGAY97CN1Ul
+QhccdnJCgM3obYkQ+fDXXvgNQLazbOVmeRd5is4TXdcNJIN3LcXXdEOa+O6X8fVT
+wxzXMD8Uy7foCRvhCrF6gqZZyt+pIJMucLzydiCJHqqDK0xjJ4wtfJNGEyIDJ82h
+UDEae9X4ao6mti146PH1zbKWS2EYaoEFeXgIqDiU8dKIZNrTd+nflIGd/TLf3LgY
+43qGEwd/oWtdI1hICu173WGdPNnZWbvJtqb4nHag2ldd+jnYHFFF0jnH+neRq4lq
+W+CAFjWz7c8JNtCr+RBVS6bYjwIDAQABoAAwDQYJKoZIhvcNAQEFBQADggIBAKYk
+c1LiB2iNqjJMPEjzJ0wpKizHoqv7Tt55Slz4Q9bTgTYmevt8SIpQCKK+ZtMxCPIy
+9tfqjsdYqBxahfXnhXUDQQOYEAKcffEZd/c3LX16kOupximycFOj0iqJDe5VZ/NJ
+7fRXlJqqkufoQQ3OqPYzS/G5mP61gadvReAkTh78StlWMNxwg6wYgQ3p4ZD1GKNI
+M7A1Z99HyJeRZcT+yx1wuyvX2MJY15Kg6m9xaM1N+q4BJ82+u76pNX+pTw6A12mB
+XhmIKqRh+KN7YGSVMtU4dBwHMlZD3CRiGUE9RFg/5aqJeXZgCVnLgzTzJUFQqRqZ
+OehYdizkI6TgTLl3xIJWHFqYc5GhY9NwghYn0BjiC/8xpOmj+soAG4hDkydejtf1
+8lFeTxW8Ga560zjvaUOqntQTLHiAnZai7lVFxE+o3/b284OYu5wWyAFh9j+eE7qP
+pnObCpeFanlsuTnQIXZD4KcojI33dmBXIVhCfe0cvtDEPLl2MYJNguO40tJwPCzL
+JBpdwqh+rBKQjX+pn56js3BgB4qrcQnWGEHWOb/Z+ooRkaIIliRLBTS5DbbnsQWp
+jyVKVhTVLNdp4L2oy94hGi9wJSCujXyBFXH4+e3HpYrZwGSJKrpnRPChm9XW520A
+EFgLzv2Oh0zXJiBJ+EtVwWfkDCImakD1u5QAHW7h
+-----END CERTIFICATE REQUEST-----
diff --git a/trunk/demo/ssl/demo@demo.key b/trunk/demo/ssl/demo@demo.key
new file mode 100644 (file)
index 0000000..b1066d8
--- /dev/null
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,955EFD4E985DC326
+
+TXxk9gZOkjLsP4MZmg0HWADP97beNLBrHBLwNvWVzFGmNcuLrAb1r6+OlwIVR/Hl
+BAFwygYvwvo2IL68I2hM+pW9wLY6WAit2CvdWs5J+q0MMc6kXilF33PvmW6G0raB
+XurT0Q0w7TcIXmjMo2k6CCnHGCUxIsYjXIKF7LdC7ERR2wCRUgtuns33/Xf7kfIg
+AEpYC5+iJFT9/ZvXm8Ezod0qG3LZ0TmRMRkWjbBOpHqWVCgTwddHZ2hCF4kNubxC
+DYL0UlDru9JgrW9ezAAloyG0GaiqPyCCkUQGw652e99OllYsfCsGQaoRNPwk2ytA
+fss7GyjymlxtjEdwWnVxngUGr+u+cp9QDD7dkSDm0uMBCVjtSg4MGvk3betn+JIt
+MYbEst4Qr0llFV7jMWt5e/EBllErrPipthfpd2o6JUxv5PNCDEbeKRSU+TZ2KuT5
+4uj8MSA8nbixQf063Q8Nz5sd9Aq4Vc+J9Juyu4fbUAI/PAL71fR3HC0hou6plxev
+EXIk6cAZLhzRKBnmwW0Srh/fp7dAlK/btQHoHVtx4E+uT2Ct+5xm63wMJLE5Z+Gw
+6xr0L9CCrnCicekEUoVdED03+U0r+lfP7nOmk/CgfIYpTP+RqsBXTVcwe1tvZqMN
+RLbL7iDCxPrRFwRs51sM2MS5v/3S4r1yTJ/ijgErhrf4shtR+AO9cqkZkLwLtTmW
+wKgWbRVnt8ITRRqqyBRjwBpmOWNC2YfkJrlDcm3rzn1fWWGOOQHlYjDYjpgVJfte
+v/HEC9Z+J82QQ73tyMUrNyJqeiyKdjUHY9T4oS13goGe3T+UUJ7ReYojmwFKCSu9
+GYNC/+7B2kPTbXVwKG9fZ85W7GdjGzy7ci7IsT95znKbPD7gmrr/5L+iYPqLv+R5
+WD9zNsyPpMZvatJRxjH2EdhhWoqvhY+M/k53AdHiM45iG6MVzKxPOlMlEsD2aiMb
+3zGUINsOm4Js77klemGqfd0uEBynypqhIQOoYnTTHnpqguandGkeyAZ689CncP3o
+pg3u3bZVoH5PubxWAFzxqpmrkk71oPaflbT6AbXGGpm9sjSPVKGm67B1yVMRtJph
+GjXuwiDRqX1HwDIMqQZrJKZv0XcEUObruKODryHuxbAA5IWTfZQoGfVei1acn5/7
+Q7MNqzDN2Rzgk4hOWuTSj3IFWzy432qfA2KQFcU37E5cBKnKq/rZp1L9O2wHY095
+4bXLugJML3pQPGg+VSw5RMhmN0RHh71W0aSQVAbOAu/fhCbrOAA/jHf8RN6gH+v2
+pZp1FZU0Bw5So/E9B2ubaTmBEfsAAnPd56jTAsHsqL8x4omXza96WGOlzqQf7W9x
+I481If4hwNl6KEeRb0guiOp0PFq9cfnjvhkbRVAiD5PodjpttAd1ZTvnJA5BDvdx
+v4JaNgKJ286I/2R65njiWtfDREwiStQzX+RjkFQGAQno3FYQiVajYg+URl8yul32
+Sn1ovaVGoT4AzM4UM6xGiAr5mQgZp75/BIYoFk3eit6025mKNfVOsljFnFhS0Bw7
+oRtfKE32TuEMmIfEMP02Ppofk3vBoLQtsgBpYLCYNy3wzzteAsXEFaAOaIFtHDXv
+TI8aHif0hgEXr6gTpQ7uqk5Kmo/9AAM8/5bbDd83Fk0m6Woi1EA0Ac2nv2TSpTeV
+MHy4vk+QJEmaDcKYCn8BlpgJe5b3CJONdt5yXGpfZISxhRQaWpv1dCSFxfARPGSy
+TPZ9jHU5fS+iegOIcb25dr0bncYy5+UKCoaW05L/z1a8Fn3AmjS5YkZBYqmsI4AX
+tkfcRKlVRGuOe3A1borQvC4pB41wlv93GKxF9xltJ7D0G/kNz9BRLvIoF7xYhzBe
+Jyp8SXUwLkAW3H1zIAf8A1rQNyP60txHXcR7HC3GGx6Y/c11L3XBzniD3VhBVZLc
+Z5ndKINz6wrmi3z87GFsBEe0aRhvXanxgBN1vOD2jhWAwOtsXAP0Pz8VZq/rCnIQ
+BKff0VVwRPqpCasgknY4hs6PmteMvOSynTS3v85a2++LrL4vE0MRMHUusY9jIUJn
+plSF9VOsjMS9eeLbch2FNnmEhnn+Sq06uZEZxgweocapm5TvU9S3b5zXWht+4ECy
+Fbe+hKcsls7B9p/sI564ylWtfy/JOsJ4GfsLaLwytbo8tUU9Z4t6bjiBXRUYBS7D
+bVep0ukngR1xeiML/887MGqN0G4RHgBhbdGvBKX0RYArMl2PTNebysuaabdYy8VB
+mrlodPL+OPrly23IHws6/48EiIugUgqTn0JtzZlAiRhK85b9dUHbnqxP9q6DOum0
+GYBnVivo+LZm+rVOPTB9SMEkZezoZZbV9US495vNQF27c85ERAXMi+qnwEmJZeff
+HqN7KzeK21coUNKrxx/n80lk0mCDX8M0BL1qQEH2iiE0wA0JZYm4cOujpmRtBJs7
+g+0Gd4qU9oQWVjbsr28wdFCBzeKvVdwi5+PVEtliSyB95ZLEFNQbOsomU986VllM
+AVYHDPmLfXVl+eUXFfdVNjnw+pXYGKhpicgJQYOunXOukdjmwu6byB7mR3e9IU/9
+BbrgWopv1ZKq/s/xvRsWih05nvXHKSeITVXUAHE4Z78Ia0ZRAj4qicJ/8XvhnJX9
+17G2nOPtPJFrOE+y+KXanBR+ZPNV8kve8hGxSzxcfvz9divRg77DCB81Jyi4LTXa
+l+p0xoM0UUS4per09zHHg9g8lHfjtaq/rge4KgV2uQENNCFjskCc8BjB9U7TYWun
+K9BNQC49p1lDESvStTDrvnX3Ckw3wsvoR4d7fGMakuZcoNF9VAH2YOi7VFBwaySF
+HB+NosmrfrK7izewfNuE751mIWPBek/5mkMwjdMl2Uch+HchGLFPMTrZSGjl8tlJ
+wzbtVP026Iutm+CDXglZq9OV0pk5NR3zF7444m9xxuzHQypRvklDd2OGwdwMoMCJ
+k370YS4dvIlXtwlW39WPyYwWyGmqpfYO2Fj5Su4Pzwz4c/LD8mUVD1scNXeE7dgD
+n1leMfMX5O1elhs9/FgNFH2ntrU0gRGORbwyD+yjryYxv3KOHic7uVY+JsAlY/JA
+ciqvTgSc49XYari0dMCE/rBAwTpKnEKDKB4pI/o6A+ARPGdSj6UThGRS4538d7cB
+-----END RSA PRIVATE KEY-----
diff --git a/trunk/demo/ssl/demo@demo.p12 b/trunk/demo/ssl/demo@demo.p12
new file mode 100644 (file)
index 0000000..9f60819
Binary files /dev/null and b/trunk/demo/ssl/demo@demo.p12 differ
diff --git a/trunk/demo/ssl/root@demo.crt b/trunk/demo/ssl/root@demo.crt
new file mode 100644 (file)
index 0000000..b79404b
--- /dev/null
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFfzCCA2cCAQMwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkRFMQ8wDQYD
+VQQIDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjEVMBMGA1UECgwMRXhhbXBsZSBH
+bWJIMQwwCgYDVQQLDANSJkQxEDAOBgNVBAMMB0RlbW8gQ0ExHTAbBgkqhkiG9w0B
+CQEWDmNhQGV4YW1wbGUub3JnMB4XDTEzMDUyODExMjgyNVoXDTIzMDUyNjExMjgy
+NVowgYQxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl
+cmxpbjEVMBMGA1UECgwMRXhhbXBsZSBHbWJIMQwwCgYDVQQLDANSJkQxDTALBgNV
+BAMMBHJvb3QxHzAdBgkqhkiG9w0BCQEWEHJvb3RAZXhhbXBsZS5vcmcwggIiMA0G
+CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDjfJpz+c2ompElZtBWC5HUJ6NevhMm
+I7VCAZjMEa8l64cCXlULOyKYbEZ2oQupg5iqHVbEK1d4UJ7b2EWTFc8bZT+cQODL
+7x3hPJ2856PNSg2Rcsna40Vy/1jieRE1gpLVZBi/myFuCBU5uQkhja+VikgIltfX
+uvYilMEkcEVSQniFJCB91xaNPWPJwuyy5JEMoRXprCjHmChaE+MGnWOXyU5+nSp7
+FuLAOZDXbNi+I8J01q2W+iWVlqvIKAa5ogq5TYayo9foVX6ftnNlkoG14gJUGn8C
+CNSAx8ZGNR16952H1UGQycDXM7T3T1MMYT8gB9qDK90ko1EgvITy2UPyWGhTxh9U
+dKkwpenVgn+hDER5ObTlDmSucCooVKZCAITimb4TjJm1pRfupaJlZfKboFmnmx2Y
+4JvZdbM7sTcCmFRv6P0UEJ3MSptzl/GBqSYFyTKmsVWq1NaRzytUKkTQP3TDpf9c
+vtJKu/CPUWKVtamf2d4U+34MLKIQ2zDRkUWDmEypjHitW9y4PlBYVtZ2ks1rdLCl
+ei7cDgwG6uwLZ9FSjHQgy0BF0lVHKnefrR+c1HZ8/vg12pDtmLvP9tL3rxWQ+fGj
+PluaIgPh/moMEKAWDHH+EeAlJ5mmutIU6HS8cL9G/doV7wfqx7TNGhbCbRIawF+x
+jBYCwiRDmNocGwIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQCm+WvHykMS7opbbfDu
+reOEt+2i/jyWuPPJB51HheyCQtV5MXyuCRflZpJJdI8iWb3DtayKCI18oK7SakcN
+hVGY+3GJyr5yYVsoeRbyFUgz4apBLb2CeEC7rHbbC39hYVc+FxIUbGiP70dm1TJW
+dZEWeyINScR/YVLitx99g6AVByRjaTDpSDr+Rbw0HWZiuJ2Sb7DqCdwaH5nJqLqN
+N8Vx4+vCCDyOD7kH7cqF3eJ6qepBIYPHib8IAqyLFDcd1OXLMQHv3rrYcQ0WM34P
+NkF7wTO1I8gzpcFyrtOBLOyx/wkoxWhqVcWzYXQrgLbgwp8Z/bJz9MyBnqX2NWq0
+qrGoRBqyLkT4W6acJ2SkgCqMLXhMXfne6wziG+nFiYexeKL+cLEB/CkarxRMEta7
+Hyb/zfWLrXATznjeeXanbgBC9f32Xo2IttLw7hPTAkOqulm7/k/U72/n21g9dLdR
+EtmfzYuDZEOH2QdBqPkrwuqY20t4qvqJITomE8GDFqguJFF5T0h0BS04oSA24J/m
+pwuaccHKuo2j7wapWhqQWZj+3CwYqZDKxIsmWCXAsreUj0GmZEKCgdOI3zzNpAI3
+F6puzfejkHze2rE9Obu71c4q0ibmo8MPx7ovoD9yIZPAvHK8HLiQaRNYP9xvv3FL
+g2zA6bTCQMYERNb1YTBv5xeuOQ==
+-----END CERTIFICATE-----
diff --git a/trunk/demo/ssl/root@demo.csr b/trunk/demo/ssl/root@demo.csr
new file mode 100644 (file)
index 0000000..34c6ced
--- /dev/null
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIEyjCCArICAQAwgYQxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzAN
+BgNVBAcMBkJlcmxpbjEVMBMGA1UECgwMRXhhbXBsZSBHbWJIMQwwCgYDVQQLDANS
+JkQxDTALBgNVBAMMBHJvb3QxHzAdBgkqhkiG9w0BCQEWEHJvb3RAZXhhbXBsZS5v
+cmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDjfJpz+c2ompElZtBW
+C5HUJ6NevhMmI7VCAZjMEa8l64cCXlULOyKYbEZ2oQupg5iqHVbEK1d4UJ7b2EWT
+Fc8bZT+cQODL7x3hPJ2856PNSg2Rcsna40Vy/1jieRE1gpLVZBi/myFuCBU5uQkh
+ja+VikgIltfXuvYilMEkcEVSQniFJCB91xaNPWPJwuyy5JEMoRXprCjHmChaE+MG
+nWOXyU5+nSp7FuLAOZDXbNi+I8J01q2W+iWVlqvIKAa5ogq5TYayo9foVX6ftnNl
+koG14gJUGn8CCNSAx8ZGNR16952H1UGQycDXM7T3T1MMYT8gB9qDK90ko1EgvITy
+2UPyWGhTxh9UdKkwpenVgn+hDER5ObTlDmSucCooVKZCAITimb4TjJm1pRfupaJl
+ZfKboFmnmx2Y4JvZdbM7sTcCmFRv6P0UEJ3MSptzl/GBqSYFyTKmsVWq1NaRzytU
+KkTQP3TDpf9cvtJKu/CPUWKVtamf2d4U+34MLKIQ2zDRkUWDmEypjHitW9y4PlBY
+VtZ2ks1rdLClei7cDgwG6uwLZ9FSjHQgy0BF0lVHKnefrR+c1HZ8/vg12pDtmLvP
+9tL3rxWQ+fGjPluaIgPh/moMEKAWDHH+EeAlJ5mmutIU6HS8cL9G/doV7wfqx7TN
+GhbCbRIawF+xjBYCwiRDmNocGwIDAQABoAAwDQYJKoZIhvcNAQEFBQADggIBALIn
+S0EvEuPFs6Ap7Pvi4ahwlzNFyRYryeAn2qkqwUyMqM2tHywxPd03jlnCTbMbSANj
+YPXIviamosY3LScyL4BrZTsayuvs0I5VKIZq612cPrpn0+hw3gK/tmiHqjEv/XhZ
+JzSAiJMQqurANhbdwEpBICnY5LjjoKcvdG+Pcto9JmXadmSfLpM25t5ldQ/azPjs
+IctBeXkHExRvT0UV5iCAxLu+kr0jxUFX9fTzewWli/TV50uCJtn7zKvQ/9WbdpZW
+mDKQSFLd2j9bDxhfmDD4A1f9/2qC/ymw9jZAdLgbs9Aan436fzZG0/pYEBxNrl5g
+LJZT+E5oXmLxlJLdPNAHSUccNzpajAaKvrwwksppE2Fqw7x0WNzNiXrsfI5xAORG
+0HIACy2K/+2wmS4Jz/FuA0llunWRhoOV9BpYlvrRL7b0IAbD0iirtakAJ/LOY1RV
+6skID/icIuP68TOy97P154Q58sXp2ic9UegjTAD04+M8+iysfA2p9/z2yWFHqEed
+tOKSe9ZfGhxaDZPmKMWTmQbReP72HLsPp/LRakN0EmxkAaRF3GscurVF90a9fbmm
+R1hKg+F8KOELcp9sgMNNhN/DA6Qwjsg6SOCf7B6nvEGBW0MSTdttZtmuQw686+gU
+2ZFgKlcmTP7epZ38lPhwiSOcObqxLwVrvbvfoxhR
+-----END CERTIFICATE REQUEST-----
diff --git a/trunk/demo/ssl/root@demo.key b/trunk/demo/ssl/root@demo.key
new file mode 100644 (file)
index 0000000..65923d7
--- /dev/null
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,4A5FB12597A1CBF1
+
+U3bL1J33MP2KEW9+AWj3YZyjpfAuhYr2c/5Zukdy/kAYRnnK436xUsKJtF9OD6H4
+QTawsgtOFL6gEKvKirbQ8q/xpWBX1xwWUSCf10QogNeRb3pk4h0G0BxM2t2Svh/b
+1iW06CDAl4E/7VAQ2Izsm9Th7Gn7uJEhMJHedDyz6vivxOPFhMA6i4OW3vWdlENZ
+cerjsUe8hE3YkQQZuF9060z4+doV8GUt0chtRorRhkH288BAppXJgqxWFG0mDHve
+EhSTDjQMbxlpS/Amnw2LgRNpBWJtOINKovXy4KzxjZPXnYIzkLF3wFRcq0/R40ND
+Wqs/L+Cc62+XXyY6ZwSYjjoTjaGWnAKfT5oDUIONb6eOOiAxAP4CbGU9m1X7Ctgy
+v4n9ElkRBvzMCnXuneB82tBL6zsEv3IvG4yf7jnPP4HL9+Dp3DtrmW4KYUT1iriE
+iC2Eq+J8TXe+F9Cj8lr95Ou/bqJQcze+5TFAPaZY5UTUqPSbxQczW3yP+Zs6mFia
+JQyUIKkQJAb0FQZYSzgs7nH76Wx0aVzpqTSnv2zZ8zULp4o+8ED2ox2eSKDPbcUa
+y0MNXMj73VGNh22WhJ/ter731TEjXSuysgBIwNZf/8CzZJCN6vSihieED1dIwa75
+a3gi9412v/EWjBJPQjcnUP7H9GyBnqJXyJaqtKuP1eGudl1n/Ri+xTx1BLRmZ0xN
+1wZN2LZaphPBd0tIOOsKfVfMfkyyFP4ymUmi9l2i/350ZPyfhldWKksaN1oGMzXC
+uQwK2NAAGMgbZRHwUEgAiVe/gZN0UyF2JRakcDK4n6woSDSvvD1mRZftb3b5saol
+9krcveZL0PJgZpO1jbw4XiiNj+1SNvfDy0nMsB8LmCtmsFAoANUWlsGZyh0xD3ko
+NE8Red5oz3Ydbw1RZglO5F0qdrZsH+p4nPOrCDttD+E8apsWXY0L4b2TkSGho2Q9
+04FZE/heKywZw+wVP8XMHvF87FP5kvpOKQEh39V9iVyCYOALMYp+Zk+lmZV+1WtW
+W5DnAhOfdXC/kDRQQLWT0SGhHOOtGLrU+KhmKlNDGe1YwjKUeKRL82+eebFIsDWe
+TRg7D3K9U+G8RwRCtsaNXptRLeG/gGb5hnP31KlIVz8QfQ9yOVtuGiGhMVlpzRYO
+prn2emRblIKASQNdYqiEg1OTZ3Hg88NLcXWUkoby6jDmA2hwWIRgVfhPrY8F2JGV
+jy5U7mmcnouVYjzBhkwSj4hMDK6Z4r5eppbEKWulEaxNI+0j+TvSBZmDEPhbtlF2
+ZO9VuHUROtBSK8EhxHgb/s8sR8jBGRGlTYHtk/djDJPmxmEvaMmoTTrZ+IqOwoMg
+CRewUi3cWFt08tvXKbprWpwGEKY9mMysYD4EijLEQGx6+HFSZ6WRMOzHAgb3kjhA
+q86YaI1y8zN6aAnRaq/UYqO/GxJMaj3svQjUwQLYoUAOp+8efs9eUVcZq4QVmAJX
+BDSh+E9GH6pCRMZcT5sETIqXfWHuoWuZTeE4qRBLIT6qCIfIr871cXclQuPRS+sI
+HFxiw0gPT+iSyIGM7pzn8kMMgbiGoSdzN90oJXUJ3OH49Liox58iyOk55LUp842N
+TnKsGgyS10qZ31MnkOw1zNnHtb458FsIXg+FjgT54Nlyf8SA0A6+tpqhPhYNfus0
+U1mjrxt1bL/q7QcjSe7FKUNenQfKHltIAAKUBrlnh3UG9a93D0V7GC8k/u+hFdWN
+kL7o8uva3NCZAXENk/ofiOpXV+wyzrf7O14T2dwQjJC6jmzK07AFL3/8k8rAUFPt
+imZk12TiB2m0mlg8RXVwsxe2PaP6rObl32wqFohOkkINfbSnTso5665LHgMtcB8j
+O7xV4sO/JZaPTe57/fyoOdqNv5MvpZaqhe4EH2mBAp04NqmcrELaux0msnrYdQnF
+7GN3PrVzpvE4jn2tZ8XSPnvawJ+cc6IYXSZrlamRKboq85V3uqhdayNEKWLDXQJ5
+NJLXxDe/9HEuOoMCxNKxScPhulXAkKN4bBAgytIcwi/9DAqDFxH8czGDzoR6aYD2
+3BIAdRkzxygy5uKBkqDSe8wugtTQI1kxvTgz06kBtN619k4dZFukQxWiAkzEZbZQ
+j3ulktFe+1oMjjQhI7S3LByWGuSrYw5kIvZrjwcwkqauk9ThTHsLrNwhKW7Dh1Op
+vCmDzi1WwMZyj6MpTEArA4YOz4UrJZohpK+8n5bPEQfy3jOJpHTilr0dpVpbEnFa
+bkj57y4ltYu1AfSgjRHtVfcXGRgj5/xrmLicDRXLH32O1al9f8bYyNFV8hLTLHUP
+q4D5dFAmVVdoRBbGroaK9fMxjpNzNqM+rHH2qLk9nE33I5LoJrqTamfCOj6a3iBh
+hGxxxmI9yNrPv2duCthEnn52haclWfbx3EJf97iIkEMjmpKnqc0KASqlRYk1pGzn
+6YhtsC+h5I1tLf/ukWgB3smsjeAWYt8bFzvcMHm8MVv6KxEthxdOh0zY3hP7peRK
++NS6HV6L6b2ci6kUkQLRhFra0SkRkMOkydHEx6d6XuKHNLt8y6yRf+O3xAOEAEIi
+QTcCSsXuLLNJCI7ouLi/IxLD4NBFJkXv5nSWZLP4woOwCF8XAUeMGRjr8iZ97VwL
+Iv1bbKwR3Ad0qQJKo5pGsWgPNceYcVuT+Lrkk0385O8e3yWen4GPsticf43NVN81
+EkQphsCOUOcJlAr2xTm6PWtVGbKQiOVVv/Ny0ixfjk4JLPzNGIaWGbQk/ywqF1VI
+RvPIIVlbGGh1FzxTHaxcvSsuPm/r3yMt0tAb5bBIb/RVuU+3MqFZWfciNurUxu/L
+YfwIc3GYrroJC08n5rBXN3fZIjr0HyAgL3c8ZSa10lqHALcoeOBSoAe4y6wmEsS8
+W1c8qsospdKERHMX6yB4qzQoqssQYtHRaPvli6Cq36yNdqRvcwjOxvzINf3SLY8N
+Lbgp7RvWbSPtmUXeubYi5V9qk3ay6YK7hXeQ2jXrRZpX2D2PB5OgUWkHXcmpx5xA
+QB/nFrx1Mz9dNCCpnBNqIhPwlJLkFYqhIwVXfoJNQtRAjaXBxKNgi+9kgQmqAaOb
+l9OUXiwv7gyepT3T3oM9Nr98b1uhom5LsJ40K2QabRJCcewJCsNCGWkziBaJOtHf
+-----END RSA PRIVATE KEY-----
diff --git a/trunk/demo/ssl/root@demo.p12 b/trunk/demo/ssl/root@demo.p12
new file mode 100644 (file)
index 0000000..d9a8603
Binary files /dev/null and b/trunk/demo/ssl/root@demo.p12 differ
diff --git a/trunk/demo/ssl/server.ks b/trunk/demo/ssl/server.ks
new file mode 100644 (file)
index 0000000..a28ade3
Binary files /dev/null and b/trunk/demo/ssl/server.ks differ
diff --git a/trunk/demo/ssl/server.ts b/trunk/demo/ssl/server.ts
new file mode 100644 (file)
index 0000000..25f7936
Binary files /dev/null and b/trunk/demo/ssl/server.ts differ
diff --git a/trunk/demo/ssl/ssl.txt b/trunk/demo/ssl/ssl.txt
new file mode 100644 (file)
index 0000000..788ab49
--- /dev/null
@@ -0,0 +1,32 @@
+# Password for all users and teh CA is 'demo'
+# Password for all key- and truststores is 'changeit'
+
+# Clean
+# rm server.*
+
+# Create CA
+openssl genrsa -des3 -out ca.key 4096
+openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
+
+# Create Keystore and Truststore and add CA to them
+keytool -import -keystore server.ts -file ca.crt -alias ArgeoDemoCA
+keytool -import -keystore server.ks -file ca.crt -alias ArgeoDemoCA
+
+# Tomcat Server
+# (we must use keytool)
+keytool -genkey -alias tomcat -keyalg RSA -keysize 4096 -keystore server.ks
+keytool -certreq -alias tomcat -keystore server.ks -file tomcat.csr
+openssl x509 -req -set_serial 02 -days 3650 -in tomcat.csr -CA ca.crt -CAkey ca.key -out tomcat.crt
+keytool -importcert -alias tomcat -keystore server.ks -file tomcat.crt
+
+# Root User
+openssl genrsa -des3 -out root@demo.key 4096 
+openssl req -new -key root@demo.key -out root@demo.csr
+openssl x509 -req -set_serial 03 -days 3650 -in root@demo.csr -CA ca.crt -CAkey ca.key -out root@demo.crt
+openssl pkcs12 -export -out root@demo.p12 -inkey root@demo.key -in root@demo.crt -certfile ca.crt
+
+# Demo User
+openssl genrsa -des3 -out demo@demo.key 4096 
+openssl req -new -key demo@demo.key -out demo@demo.csr
+openssl x509 -req -set_serial 04 -days 3650 -in demo@demo.csr -CA ca.crt -CAkey ca.key -out demo@demo.crt
+openssl pkcs12 -export -out demo@demo.p12 -inkey demo@demo.key -in demo@demo.crt -certfile ca.crt
diff --git a/trunk/demo/ssl/tomcat.crt b/trunk/demo/ssl/tomcat.crt
new file mode 100644 (file)
index 0000000..0ce674b
--- /dev/null
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFYjCCA0oCAQIwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkRFMQ8wDQYD
+VQQIDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjEVMBMGA1UECgwMRXhhbXBsZSBH
+bWJIMQwwCgYDVQQLDANSJkQxEDAOBgNVBAMMB0RlbW8gQ0ExHTAbBgkqhkiG9w0B
+CQEWDmNhQGV4YW1wbGUub3JnMB4XDTEzMDUyODExMjYwM1oXDTIzMDUyNjExMjYw
+M1owaDELMAkGA1UEBhMCREUxDzANBgNVBAgTBkJlcmxpbjEPMA0GA1UEBxMGQmVy
+bGluMRUwEwYDVQQKEwxFeGFtcGxlIEdtYkgxDDAKBgNVBAsMA1ImRDESMBAGA1UE
+AxMJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAj0X1
+BD4zndTvh5i+ZI+/PJWNhRQYVh+JMFSc85z/APrqZNbwsOEg2mjyLk+bTUcxSZtA
+JLOBGUp7cwQTLD7VTtW7SEtbrcPdikRFQaTL4MNSZNysCPFTOnaPmkHTqnfrNDq2
+yMoaIDp/73dkefT2hoafy6Of1ZC+Sp8QvVORAsnyauRrSnrQSeQlRLm7i2H8FfXK
+zJm33v7LBoX+xJrpKE0fPvJgTsrUaMH76ytMVvDn+PYrW42lmjnAuWZmPJsCjRX7
+XwPggQ6Sdmzj7Z0XeEX3W2ZAMFP2qhbVVSzS4lOUir7/VwfSHmkfhjR8ElzOw6t+
+wG116OXX+yk0INsP/0pLoo8N7yagyrajYcIO5Il7hxVvG5ToHzwgGEn+rMDr1KrF
+f/4XX55Hx2uw7mzkmpyyUVHCyhkNQUwnEmAXKaRM6TH2k84t3TJD/TapiugOGy+a
+o4cKfqfrRDWg09dk4+l4t+BZdlQ2qs/3Umt1aUGar0CLgRhmQJUvFfYCbTFJH4N8
+TVUE1C1C+anIXlapSZCfe9Nfbj1l8yWZwhhMMKaAjsdcBw1upi+cvPuvNTgu1CUz
+uNuPrYTMVkUxbAvZXE0OClZK8uFhlKD+wPzQOOinH7+xXGpAWoAbs5SckuqD7vIz
+mFA9DcllDRT7eQO5xEdfT1jg/PawS1mY8yp1GysCAwEAATANBgkqhkiG9w0BAQUF
+AAOCAgEAQQJm0wDgnsU4caIYt6LGIvPZtuIUfeCy3ZkM0LJsv3JYj8ppb4ULVknM
+8LNPk3W3BAbnuIZR5E9dkByfu2PF/fNpqJRLpCR5zSN3oQsQjHrf9XAr5VeZ0E/w
+YR/udjTJoXQVm2YhtOhDGJ52TW1TZtNGXrn5kmnkWgqm6WSXIZSQ1viW6a6nklZC
+8Tt0o0l+KiiMapgvC9eNonx3CpM+EzSAASmqUz+uPUG0SIQQfuP0Fs5oYaI/I2F3
+j5WxLBdqjTaatwkrhBV22ZoXigWpTNTHcpc469djr23ie+iHlIO/YkL41DDOI5r9
+EsIKxQBKzO9addeys5gReAiEMhDTYGwflliWhdFMAM1mLB4YJjN1iSJEkWkDGg4G
+N+i/Ydx5fgExAws6tE2nUxTBt/F/6Qe+oWCURLE2YDID+t5z7/JQEFJgnboLsUoe
+epKbITPtzNCnnWRwsqsanlx/RbgtI6Flb6/CWGmzlg1y6XhQDO0D6/4amvjeeqVV
+a3vSVpL06K7/PxDFK8vnJmVcC8SqN5RBwsngMnMpPOjD6TNdXo6R45m7UMjQiFO+
+XLi7SSvngmNsaVOlX3adb77ql6DS4dNLZ0UNi6/fr3ADkdkk6yewNJBgVpFoyNPn
+yZdafIUvaRcrH6QXhRyrDLRhF6j9AJNIqUwDBQ1IhexYz/23r6o=
+-----END CERTIFICATE-----
diff --git a/trunk/demo/ssl/tomcat.csr b/trunk/demo/ssl/tomcat.csr
new file mode 100644 (file)
index 0000000..df66bea
--- /dev/null
@@ -0,0 +1,24 @@
+-----BEGIN NEW CERTIFICATE REQUEST-----
+MIIErTCCApUCAQAwaDELMAkGA1UEBhMCREUxDzANBgNVBAgTBkJlcmxpbjEPMA0GA1UEBxMGQmVy
+bGluMRUwEwYDVQQKEwxFeGFtcGxlIEdtYkgxDDAKBgNVBAsMA1ImRDESMBAGA1UEAxMJbG9jYWxo
+b3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAj0X1BD4zndTvh5i+ZI+/PJWNhRQY
+Vh+JMFSc85z/APrqZNbwsOEg2mjyLk+bTUcxSZtAJLOBGUp7cwQTLD7VTtW7SEtbrcPdikRFQaTL
+4MNSZNysCPFTOnaPmkHTqnfrNDq2yMoaIDp/73dkefT2hoafy6Of1ZC+Sp8QvVORAsnyauRrSnrQ
+SeQlRLm7i2H8FfXKzJm33v7LBoX+xJrpKE0fPvJgTsrUaMH76ytMVvDn+PYrW42lmjnAuWZmPJsC
+jRX7XwPggQ6Sdmzj7Z0XeEX3W2ZAMFP2qhbVVSzS4lOUir7/VwfSHmkfhjR8ElzOw6t+wG116OXX
++yk0INsP/0pLoo8N7yagyrajYcIO5Il7hxVvG5ToHzwgGEn+rMDr1KrFf/4XX55Hx2uw7mzkmpyy
+UVHCyhkNQUwnEmAXKaRM6TH2k84t3TJD/TapiugOGy+ao4cKfqfrRDWg09dk4+l4t+BZdlQ2qs/3
+Umt1aUGar0CLgRhmQJUvFfYCbTFJH4N8TVUE1C1C+anIXlapSZCfe9Nfbj1l8yWZwhhMMKaAjsdc
+Bw1upi+cvPuvNTgu1CUzuNuPrYTMVkUxbAvZXE0OClZK8uFhlKD+wPzQOOinH7+xXGpAWoAbs5Sc
+kuqD7vIzmFA9DcllDRT7eQO5xEdfT1jg/PawS1mY8yp1GysCAwEAAaAAMA0GCSqGSIb3DQEBBQUA
+A4ICAQCORSPE6s/ogDnCwX4KDkk8srvdkuERiC3Hb6vTP0bVkLRwHdj77xGNwkXI7UasE52ykOze
+khMuk94onH8yyeDg57EXO4267AsqowV6Od94AGKTndx4Zosx2N+JOGGA0ZwCHvmoX1Wwe1KJ6QoI
+uMdpO+i9uo4ZYth76VV+Yn3mtyJAH0sdHeFkgevKLDURtC+m70XF77NKl+L7VuoNKxXaVCab3d/x
+aksTJpwLGt8QECR1Wq5FPNG/EQiFqQCd3WyzgsebGLDHYQgPEKkKKhMU3G/kIofutz/hNvxp8MRK
+EEEnyfZWiSpYxxmEPmelyinOkoOH5tqbkHzcPawSPFWDBR4pWAg9efdl7zdVAxzNkS5PqWtXrWvm
+jfMAPkTJoeQ6YWZZjHxpYrbBZfIgCr9VehGdzvhDVGbe8NeTDzwk/AItr5shIbFZpA0vIHB4+wEA
+QVl7d1ZM/0qEKGLeHG8TvF4TUIBE1C6RVpnP5jMB9pLQ4FEbgHaadGxQfGxh18GfyxlWmQy8RgDW
+BDJRPClofXm57665hD+py7Jw5F4ZgD5IKwU96kDe7mZRLkF66ZinoubZeyXZBX4N0p7lrWzd/mJj
+r3Yf69L/Ptct7Cr3c4z7Y1xqrBb88bKhVRCfJVtlvE9mSvMh6UUY1QlAGQaDmSQEB+eCO+8diaTa
+gEOeNw==
+-----END NEW CERTIFICATE REQUEST-----
diff --git a/trunk/doc/files/etc/yum.repos.d/argeo-staging.repo b/trunk/doc/files/etc/yum.repos.d/argeo-staging.repo
new file mode 100644 (file)
index 0000000..f15e34d
--- /dev/null
@@ -0,0 +1,6 @@
+[argeo-osgi-staging]
+name=Argeo OSGi Staging
+baseurl=file:///srv/rpmfactory/argeo-osgi-1-staging/6/x86_64
+gpgcheck=0
+http_caching=none
+metadata_expire=0
diff --git a/trunk/doc/pom.xml b/trunk/doc/pom.xml
new file mode 100644 (file)
index 0000000..5bf01e7
--- /dev/null
@@ -0,0 +1,41 @@
+<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>1.1.8-SNAPSHOT</version>
+               <artifactId>argeo-commons</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>doc</artifactId>
+       <name>Commons Doc</name>
+       <packaging>pom</packaging>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>com.agilejava.docbkx</groupId>
+                               <artifactId>docbkx-maven-plugin</artifactId>
+                               <configuration>
+                                       <htmlStylesheet>css/style.css</htmlStylesheet>
+                                       <postProcess>
+                                               <!-- See https://fisheye.springframework.org/rdiff/spring-ldap?csid=439&u&N -->
+                                               <copy todir="target/docbkx/html">
+                                                       <fileset dir="src/docbkx">
+                                                               <include name="*/*.css" />
+                                                       </fileset>
+                                               </copy>
+                                       </postProcess>
+                               </configuration>
+                               <executions>
+                                       <execution>
+                                               <goals>
+                                                       <goal>generate-html</goal>
+                                                       <goal>generate-pdf</goal>
+                                               </goals>
+                                               <phase>pre-site</phase>
+                                       </execution>
+                               </executions>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
diff --git a/trunk/doc/reference/commons-gettingStarted.xml b/trunk/doc/reference/commons-gettingStarted.xml
new file mode 100644 (file)
index 0000000..2b680e1
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+                 "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+
+<chapter label="1" id="gettingStarted">
+       <title>Getting started</title>
+
+       <section label="1.1" id="deveptEnvironment">
+               <title>Development environment</title>\r
+               <para>The following instructions are about setting up an Argeo\r
+                       development environment.</para>
+
+               <section label="1.1.1" id="softwareRequirements">
+                       <title>Software requirements</title>\r
+                       <para>\r
+                               The following elements are required to install the development\r
+                               environment:\r
+                               <itemizedlist spacing="compact">\r
+                                       <listitem>\r
+                                               <para>Java</para>\r
+                                       </listitem>\r
+                                       <listitem>\r
+                                               <para>Eclipse</para>\r
+                                       </listitem>\r
+                               </itemizedlist>\r
+                       </para>\r
+               </section>\r
+\r
+               <section label="1.1.2" id="clientInstallation">\r
+                       <title>Client installation</title>
+
+                       <para></para>\r
+               </section>\r
+       </section>
+</chapter>
diff --git a/trunk/doc/reference/css/style.css b/trunk/doc/reference/css/style.css
new file mode 100644 (file)
index 0000000..0f446b9
--- /dev/null
@@ -0,0 +1,303 @@
+body {
+   text-align: justify;
+   margin-right: 2em;
+   margin-left: 2em;
+}
+
+a,
+a[accesskey^="h"],
+a[accesskey^="n"],
+a[accesskey^="u"],
+a[accesskey^="p"] {
+    font-family: Verdana, Arial, helvetica, sans-serif;
+    font-size: 12px;
+       color: #003399;
+}
+
+a:active {
+    color: #003399;
+}
+
+a:visited {
+    color: #888888;
+}
+
+p {
+       font-family: Verdana, Arial, sans-serif;
+}
+
+dt {
+       font-family: Verdana, Arial, sans-serif;
+       font-size: 12px;
+}
+
+p, dl, dt, dd, blockquote {
+    color: #000000;
+    margin-bottom: 3px;
+    margin-top: 3px;
+    padding-top: 0;
+}
+
+ol, ul, p {
+    margin-top: 6px;
+    margin-bottom: 6px;
+}
+
+p, blockquote {
+    font-size: 90%;
+}
+
+p.releaseinfo {
+    font-size: 100%;
+    font-weight: bold;
+    font-family: Verdana, Arial, helvetica, sans-serif;
+    padding-top: 10px;
+}
+
+p.pubdate {
+    font-size: 120%;
+    font-weight: bold; 
+    font-family: Verdana, Arial, helvetica, sans-serif;
+}
+
+td {
+    font-size: 80%;
+}
+
+td, th, span {
+    color: #000000;
+}
+
+td[width^="40%"] {
+    font-family: Verdana, Arial, helvetica, sans-serif;
+    font-size: 12px;
+       color: #003399;
+}
+
+table[summary^="Navigation header"] tbody tr th[colspan^="3"] {
+    font-family: Verdana, Arial, helvetica, sans-serif;
+}
+
+blockquote {
+    margin-right: 0;
+}
+
+h1, h2, h3, h4, h6 {
+    color: #000000;
+    font-weight: 500;
+    margin-top: 0;
+    padding-top: 14px;
+    font-family: Verdana, Arial, helvetica, sans-serif;
+    margin-bottom: 0;
+}
+
+h2.title {
+    font-weight: 800;
+    margin-bottom: 8px;
+}
+
+h2.subtitle {
+    font-weight: 800;
+    margin-bottom: 20px;
+}
+
+.firstname, .surname {
+       font-size: 12px;
+    font-family: Verdana, Arial, helvetica, sans-serif;
+}
+
+table {
+    border-collapse: collapse;
+    border-spacing: 0;
+    border: 1px black;
+    empty-cells: hide;
+    margin: 10px 0 30px 50px;
+    width: 90%;
+}
+
+div.table {
+       margin: 30px 0 10px 0;
+       border: 1px dashed gray;
+       padding: 10px;
+}
+
+div .table-contents table {
+       border: 1px solid black;
+}
+
+div.table > p.title {
+       padding-left: 10px;
+}
+
+table[summary^="Navigation footer"] {
+    border-collapse: collapse;
+    border-spacing: 0;
+    border: 1px black;
+    empty-cells: hide;    
+    margin: 0px;
+    width: 100%;
+}
+
+table[summary^="Note"],
+table[summary^="Warning"],
+table[summary^="Tip"] {
+    border-collapse: collapse;
+    border-spacing: 0;
+    border: 1px black;
+    empty-cells: hide;    
+    margin: 10px 0px 10px -20px;
+    width: 100%;
+}
+
+td {
+    padding: 4pt;
+    font-family: Verdana, Arial, helvetica, sans-serif;
+}
+
+div.warning TD {
+       text-align: justify;
+}
+
+h1 { 
+    font-size: 150%; 
+}
+
+h2 { 
+    font-size: 110%; 
+}
+
+h3 {
+    font-size: 100%; font-weight: bold; 
+}
+
+h4 { 
+    font-size: 90%; font-weight: bold;
+}
+
+h5 {
+    font-size: 90%; font-style: italic; 
+}
+
+h6 { 
+    font-size: 100%; font-style: italic; 
+}
+
+tt {
+    font-size: 110%;
+    font-family: "Courier New", Courier, monospace;
+    color: #000000;
+}
+
+.navheader, .navfooter {
+       border: none;
+}
+
+div.navfooter table {
+       border-style: dashed;
+       border-color: gray;
+       border-width: 1px 1px 1px 1px;
+       background-color: #cde48d;
+}
+
+pre {
+    font-size: 110%;
+    padding: 5px;
+    border-style: solid;
+    border-width: 1px;
+    border-color: #CCCCCC;
+    background-color: #f3f5e9;
+}
+
+ul, ol, li {
+    list-style: disc;
+}
+
+hr {
+    width: 100%;
+    height: 1px;
+    background-color: #CCCCCC;
+    border-width: 0;
+    padding: 0;
+}
+
+.variablelist { 
+    padding-top: 10px; 
+    padding-bottom: 10px; 
+    margin: 0;
+}
+
+.term { 
+    font-weight:bold;
+}
+
+.mediaobject {
+    padding-top: 30px; 
+    padding-bottom: 30px; 
+}
+
+.legalnotice {
+    font-family: Verdana, Arial, helvetica, sans-serif;
+    font-size: 12px;
+    font-style: italic;
+}
+
+.sidebar {
+    float: right;
+    margin: 10px 0 10px 30px;
+    padding: 10px 20px 20px 20px;
+    width: 33%;
+    border: 1px solid black;
+    background-color: #F4F4F4;
+    font-size: 14px;
+}
+
+.property {
+       font-family: "Courier New", Courier, monospace;
+}
+
+a code {
+       font-family: Verdana, Arial, monospace;
+       font-size: 12px;
+}
+
+td code {
+    font-size: 110%;
+}
+
+div.note * td,
+div.tip * td,
+div.warning * td,
+div.calloutlist * td {
+       text-align: justify;
+       font-size: 100%;
+}
+
+.programlisting {
+   clear: both;
+}
+
+.programlisting .interfacename,
+.programlisting .literal,
+.programlisting .classname {
+    font-size: 95%;
+}
+
+.title .interfacename,
+.title .literal,
+.title .classname {
+    font-size: 130%;
+}
+
+/* everything in a <lineannotation/> is displayed in a coloured, comment-like font */
+.programlisting * .lineannotation,
+.programlisting * .lineannotation * {
+       color: green;
+}
+
+.question * p {
+    font-size: 100%;
+}
+
+.answer * p {
+    font-size: 100%;
+}
\ No newline at end of file
diff --git a/trunk/doc/site/apt/index.apt b/trunk/doc/site/apt/index.apt
new file mode 100644 (file)
index 0000000..3753a3e
--- /dev/null
@@ -0,0 +1,3 @@
+Argeo Commons
+
+  Commons groups the generic layers developed and maintained by Argeo. They factorize complex but generic logic in fields like security, default configurations, etc.
diff --git a/trunk/doc/site/site.xml b/trunk/doc/site/site.xml
new file mode 100644 (file)
index 0000000..af6484f
--- /dev/null
@@ -0,0 +1,40 @@
+<!--
+
+    Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
+
+    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.
+
+-->
+
+<project xmlns="http://maven.apache.org/DECORATION/1.0.0"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/DECORATION/1.0.0 http://maven.apache.org/xsd/decoration-1.0.0.xsd">
+       <version />
+       <publishDate format="yyyy-MM-dd HH:mm" />
+       <body>
+               <menu name="Overview">
+                       <item name="Home" href="index.html" />
+                       <!--<item name="Reference Documentation" href="reference/index.html" />-->
+                       <item name="API (Javadoc)" href="apidocs/index.html" target="argeo_commons_javadoc" />
+                       <item name="Browse Code" href="xref/index.html" target="argeo_commons_code" />
+                       <item name="FIXMEs / TODOs" href="taglist.html" />
+               </menu>
+               <menu ref="reports" />
+       </body>
+       <skin>
+               <groupId>org.apache.maven.skins</groupId>
+               <artifactId>maven-default-skin</artifactId>
+               <version>1.0</version>
+       </skin>
+</project>
+
diff --git a/trunk/license-apache2-header.txt b/trunk/license-apache2-header.txt
new file mode 100644 (file)
index 0000000..be033d0
--- /dev/null
@@ -0,0 +1,13 @@
+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.
\ No newline at end of file
diff --git a/trunk/pom.xml b/trunk/pom.xml
new file mode 100644 (file)
index 0000000..4cae9fd
--- /dev/null
@@ -0,0 +1,590 @@
+<?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">
+       <modelVersion>4.0.0</modelVersion>
+       <groupId>org.argeo.commons</groupId>
+       <artifactId>argeo-commons</artifactId>
+       <version>2.1.11</version>
+       <name>Argeo Commons</name>
+       <description>Generic layers integrating free / open source technologies in order to quickly develop custom enterprise systems: security, JCR, user interface, monitoring</description>
+       <packaging>pom</packaging>
+       <properties>
+               <version.argeo-commons>2.1.11-SNAPSHOT</version.argeo-commons>
+               <developmentCycle.argeo-commons>2.1</developmentCycle.argeo-commons>
+               <developmentCycle.argeo-commons.startDate>2012-12-19</developmentCycle.argeo-commons.startDate>
+               <version.argeo-distribution>1.4.0</version.argeo-distribution>
+               <version.rap>2.2.0-R-20131204-0942</version.rap>
+               <version.rap.addons>0.5.0.argeo.20141029</version.rap.addons>
+               <!-- RPM -->
+               <rpm.release>6</rpm.release>
+               <rpm.stagingRepository>/srv/rpmfactory/argeo-osgi-2-staging/6/x86_64</rpm.stagingRepository>
+               <!-- encoding, see http://is.gd/mvn_source_encoding -->
+               <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+       </properties>
+       <modules>
+               <module>base</module>
+               <module>server</module>
+               <module>security</module>
+       </modules>
+       <organization>
+               <name>Argeo</name>
+               <url>http://www.argeo.org</url>
+       </organization>
+       <url>http://projects.argeo.org/commons/</url>
+       <scm>
+               <connection>scm:svn:https://svn.argeo.org/commons/tags/argeo-commons-2.1.11</connection>
+               <developerConnection>scm:svn:https://svn.argeo.org/commons/tags/argeo-commons-2.1.11</developerConnection>
+               <url>https://svn.argeo.org/commons/tags/argeo-commons-2.1.11</url>
+       </scm>
+       <inceptionYear>2007</inceptionYear>
+       <licenses>
+               <license>
+                       <name>Apache 2</name>
+                       <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+                       <distribution>repo</distribution>
+                       <comments><![CDATA[
+Argeo Commons Enterprise Framework
+                          
+Copyright (C) 2007-2012 Argeo GmbH
+
+Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+]]>
+                       </comments>
+               </license>
+       </licenses>
+       <issueManagement>
+               <system>Bugzilla</system>
+               <url>https://bugzilla.argeo.org</url>
+       </issueManagement>
+       <developers>
+               <developer>
+                       <id>mbaudier</id>
+                       <name>Mathieu Baudier</name>
+                       <email><![CDATA[http://mailhide.recaptcha.net/d?k=01EM7GpnvY3k8woQ2tnnZLUA==&c=crsNpHjhOBDPswHG6HD_gXaqymhC69wmBf7wlagcSHw=]]></email>
+                       <organization>Argeo</organization>
+                       <organizationUrl>http://www.argeo.org</organizationUrl>
+                       <roles>
+                               <role>architect</role>
+                               <role>developer</role>
+                               <role>QA</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <id>bsinou</id>
+                       <name>Bruno Sinou</name>
+                       <email><![CDATA[http://www.google.com/recaptcha/mailhide/d?k=01SZoYvDnJzcw0KOR7M7u6Qg==&c=SVgEjXA_Uu9ZrNzLES92w1ght6puLFiVpoNUddCfSU8=]]></email>
+                       <organization>Argeo</organization>
+                       <organizationUrl>http://www.argeo.org</organizationUrl>
+                       <roles>
+                               <role>developer</role>
+                       </roles>
+               </developer>
+       </developers>
+       <build>
+               <extensions>
+                       <extension>
+                               <groupId>org.apache.maven.wagon</groupId>
+                               <artifactId>wagon-webdav-jackrabbit</artifactId>
+                               <version>2.2</version>
+                       </extension>
+               </extensions>
+               <resources>
+                       <resource>
+                               <directory>src/main/resources</directory>
+                       </resource>
+                       <resource>
+                               <directory>.</directory>
+                               <includes>
+                                       <include>plugin.xml</include>
+                                       <include>META-INF/**</include>
+                                       <include>WEB-INF/**</include>
+                                       <include>icons/**</include>
+                                       <include>branding/**</include>
+                                       <include>img/**</include>
+                                       <include>theme/**</include>
+                                       <include>*.properties</include>
+                                       <include>properties/*.properties</include>
+                                       <include>p2.inf</include>
+                               </includes>
+                               <excludes>
+                                       <exclude>build.properties</exclude>
+                               </excludes>
+                       </resource>
+               </resources>
+               <pluginManagement>
+                       <plugins>
+                               <!-- Maven -->
+                               <plugin>
+                                       <artifactId>maven-compiler-plugin</artifactId>
+                                       <version>2.3.2</version>
+                                       <configuration>
+                                               <source>1.6</source>
+                                               <target>1.6</target>
+                                       </configuration>
+                               </plugin>
+                               <plugin>
+                                       <artifactId>maven-source-plugin</artifactId>
+                                       <version>2.1.2</version>
+                                       <executions>
+                                               <execution>
+                                                       <id>attach-sources</id>
+                                                       <phase>package</phase>
+                                                       <goals>
+                                                               <goal>jar</goal>
+                                                       </goals>
+                                               </execution>
+                                       </executions>
+                               </plugin>
+                               <plugin>
+                                       <artifactId>maven-clean-plugin</artifactId>
+                                       <version>2.4.1</version>
+                                       <configuration>
+                                               <filesets>
+                                                       <fileset>
+                                                               <directory>META-INF</directory>
+                                                               <includes>
+                                                                       <include>MANIFEST.MF</include>
+                                                               </includes>
+                                                       </fileset>
+                                               </filesets>
+                                       </configuration>
+                               </plugin>
+                               <plugin>
+                                       <artifactId>maven-surefire-plugin</artifactId>
+                                       <version>2.12</version>
+                               </plugin>
+                               <plugin>
+                                       <artifactId>maven-jar-plugin</artifactId>
+                                       <version>2.4</version>
+                                       <configuration>
+                                               <archive>
+                                                       <manifestFile>META-INF/MANIFEST.MF</manifestFile>
+                                               </archive>
+                                       </configuration>
+                               </plugin>
+                               <plugin>
+                                       <artifactId>maven-antrun-plugin</artifactId>
+                                       <version>1.7</version>
+                               </plugin>
+                               <plugin>
+                                       <artifactId>maven-resources-plugin</artifactId>
+                                       <version>2.5</version>
+                               </plugin>
+                               <plugin>
+                                       <artifactId>maven-dependency-plugin</artifactId>
+                                       <version>2.4</version>
+                               </plugin>
+                               <plugin>
+                                       <artifactId>maven-release-plugin</artifactId>
+                                       <version>2.2.2</version>
+                                       <configuration>
+                                               <autoVersionSubmodules>true</autoVersionSubmodules>
+                                       </configuration>
+                               </plugin>
+                               <plugin>
+                                       <artifactId>maven-javadoc-plugin</artifactId>
+                                       <version>2.8.1</version>
+                               </plugin>
+                               <!-- Apache -->
+                               <plugin>
+                                       <groupId>org.apache.felix</groupId>
+                                       <artifactId>maven-bundle-plugin</artifactId>
+                                       <version>2.3.7</version>
+                                       <extensions>true</extensions>
+                                       <configuration>
+                                               <manifestLocation>META-INF</manifestLocation>
+                                               <instructions>
+                                                       <Bundle-Version>${project.version}-r${timestamp}</Bundle-Version>
+                                                       <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+                                                       <Bundle-RequiredExecutionEnvironment>JavaSE-1.6</Bundle-RequiredExecutionEnvironment>
+                                                       <!-- SLC specific instructions -->
+                                                       <SLC-GroupId>${project.groupId}</SLC-GroupId>
+                                                       <_removeheaders>Bnd-LastModified,Build-Jdk,Built-By,Tool,Created-By</_removeheaders>
+                                               </instructions>
+                                       </configuration>
+                                       <executions>
+                                               <execution>
+                                                       <id>bundle-manifest</id>
+                                                       <phase>process-classes</phase>
+                                                       <goals>
+                                                               <goal>manifest</goal>
+                                                       </goals>
+                                               </execution>
+                                       </executions>
+                               </plugin>
+                               <!-- Codehaus -->
+                               <plugin>
+                                       <groupId>org.codehaus.mojo</groupId>
+                                       <artifactId>buildnumber-maven-plugin</artifactId>
+                                       <version>1.0</version>
+                               </plugin>
+                               <plugin>
+                                       <groupId>org.codehaus.mojo</groupId>
+                                       <artifactId>rpm-maven-plugin</artifactId>
+                                       <version>2.1-alpha-1</version>
+                                       <extensions>true</extensions>
+                                       <configuration>
+                                               <version>${project.version}</version>
+                                               <release>r${timestamp}.el${rpm.release}</release>
+                                               <distribution>argeo${rpm.release}</distribution>
+                                               <packager>mbaudier@argeo.org</packager>
+                                               <group>Applications/System</group>
+                                               <prefix>/usr</prefix>
+                                               <copyright>2012 Argeo GmbH and others</copyright>
+                                       </configuration>
+                               </plugin>
+                               <plugin>
+                                       <groupId>org.codehaus.mojo</groupId>
+                                       <artifactId>exec-maven-plugin</artifactId>
+                                       <version>1.2.1</version>
+                               </plugin>
+                               <!-- Argeo -->
+                               <plugin>
+                                       <groupId>org.argeo.maven.plugins</groupId>
+                                       <artifactId>maven-argeo-osgi-plugin</artifactId>
+                                       <version>1.1.2</version>
+                               </plugin>
+                               <!-- Others -->
+                               <plugin>
+                                       <groupId>com.mycila.maven-license-plugin</groupId>
+                                       <artifactId>maven-license-plugin</artifactId>
+                                       <version>1.9.0</version>
+                               </plugin>
+                               <plugin>
+                                       <groupId>com.agilejava.docbkx</groupId>
+                                       <artifactId>docbkx-maven-plugin</artifactId>
+                                       <version>2.0.14</version>
+                               </plugin>
+                               <!-- Site -->
+                               <plugin>
+                                       <artifactId>maven-site-plugin</artifactId>
+                                       <version>3.0</version>
+                                       <configuration>
+                                               <siteDirectory>doc/site</siteDirectory>
+                                               <generateSitemap>true</generateSitemap>
+                                               <skip>true</skip>
+                                               <reportPlugins>
+                                                       <plugin>
+                                                               <groupId>org.apache.maven.plugins</groupId>
+                                                               <artifactId>maven-project-info-reports-plugin</artifactId>
+                                                               <version>2.4</version>
+                                                               <reportSets>
+                                                                       <reportSet>
+                                                                               <reports>
+                                                                                       <report>index</report>
+                                                                                       <report>summary</report>
+                                                                                       <report>license</report>
+                                                                                       <report>scm</report>
+                                                                                       <report>issue-tracking</report>
+                                                                                       <report>project-team</report>
+                                                                               </reports>
+                                                                       </reportSet>
+                                                               </reportSets>
+                                                       </plugin>
+                                                       <plugin>
+                                                               <artifactId>maven-javadoc-plugin</artifactId>
+                                                               <version>2.8.1</version>
+                                                               <configuration>
+                                                                       <encoding>UTF-8</encoding>
+                                                                       <excludePackageNames>*.demo.*:*.internal.*</excludePackageNames>
+                                                                       <detectLinks>true</detectLinks>
+                                                                       <links>
+                                                                               <!-- Java -->
+                                                                               <link>http://docs.oracle.com/javase/6/docs/api</link>
+                                                                               <!-- OSGi -->
+                                                                               <link>http://www.osgi.org/javadoc/r4v42</link>
+                                                                               <!-- Spring -->
+                                                                               <link>http://static.springsource.org/spring/docs/2.5.x/api</link>
+                                                                               <link>http://static.springsource.org/osgi/docs/1.2.x/api</link>
+                                                                               <link>http://static.springsource.org/spring-security/site/docs/2.0.x/apidocs</link>
+                                                                               <!-- JCR -->
+                                                                               <link>http://www.day.com/maven/javax.jcr/javadocs/jcr-2.0</link>
+                                                                               <!-- Eclipse -->
+                                                                               <link>http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api</link>
+                                                                       </links>
+                                                               </configuration>
+                                                               <reportSets>
+                                                                       <reportSet>
+                                                                               <id>aggregate</id>
+                                                                               <reports>
+                                                                                       <report>aggregate</report>
+                                                                               </reports>
+                                                                       </reportSet>
+                                                               </reportSets>
+                                                       </plugin>
+                                                       <plugin>
+                                                               <artifactId>maven-jxr-plugin</artifactId>
+                                                               <version>2.3</version>
+                                                               <configuration>
+                                                                       <excludes>
+                                                                               <exclude>**/demo/**/*</exclude>
+                                                                               <exclude>**/internal/**/*</exclude>
+                                                                       </excludes>
+                                                               </configuration>
+                                                               <reportSets>
+                                                                       <reportSet>
+                                                                               <id>aggregate</id>
+                                                                               <reports>
+                                                                                       <report>aggregate</report>
+                                                                               </reports>
+                                                                       </reportSet>
+                                                               </reportSets>
+                                                       </plugin>
+                                                       <plugin>
+                                                               <groupId>org.codehaus.mojo</groupId>
+                                                               <artifactId>taglist-maven-plugin</artifactId>
+                                                               <version>2.4</version>
+                                                               <configuration>
+                                                                       <aggregate>true</aggregate>
+                                                                       <tags>
+                                                                               <tag>TODO</tag>
+                                                                               <tag>FIXME</tag>
+                                                                       </tags>
+                                                               </configuration>
+                                                       </plugin>
+                                                       <plugin>
+                                                               <artifactId>maven-changelog-plugin</artifactId>
+                                                               <version>2.2</version>
+                                                               <configuration>
+                                                                       <type>date</type>
+                                                                       <dates>
+                                                                               <date>${developmentCycle.argeo-commons.startDate}</date>
+                                                                       </dates>
+                                                                       <dateFormat>yyyy-MM-dd</dateFormat>
+                                                               </configuration>
+                                                       </plugin>
+                                               </reportPlugins>
+                                       </configuration>
+                               </plugin>
+                       </plugins>
+               </pluginManagement>
+               <plugins>
+                       <plugin>
+                               <groupId>org.codehaus.mojo</groupId>
+                               <artifactId>buildnumber-maven-plugin</artifactId>
+                               <executions>
+                                       <execution>
+                                               <phase>generate-resources</phase>
+                                               <goals>
+                                                       <goal>create-timestamp</goal>
+                                               </goals>
+                                       </execution>
+                               </executions>
+                               <configuration>
+                                       <doCheck>false</doCheck>
+                                       <doUpdate>false</doUpdate>
+                                       <timestampFormat>yyMMdd_HHmm</timestampFormat>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <artifactId>maven-clean-plugin</artifactId>
+                               <configuration>
+                                       <filesets>
+                                               <fileset>
+                                                       <directory>META-INF</directory>
+                                                       <includes>
+                                                               <include>MANIFEST.MF</include>
+                                                       </includes>
+                                               </fileset>
+                                       </filesets>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <artifactId>maven-javadoc-plugin</artifactId>
+                               <configuration>
+                                       <skip>true</skip>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <artifactId>maven-resources-plugin</artifactId>
+                               <configuration>
+                                       <encoding>UTF-8</encoding>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <artifactId>maven-site-plugin</artifactId>
+                               <inherited>false</inherited>
+                               <configuration>
+                                       <skip>false</skip>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <artifactId>maven-release-plugin</artifactId>
+                               <configuration>
+                                       <goals>deploy</goals>
+                                       <releaseProfiles>rpmbuild,rpmbuild-tp</releaseProfiles>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>com.mycila.maven-license-plugin</groupId>
+                               <artifactId>maven-license-plugin</artifactId>
+                               <configuration>
+                                       <header>license-apache2-header.txt</header>
+                                       <failIfMissing>true</failIfMissing>
+                                       <aggregate>true</aggregate>
+                                       <includes>
+                                               <include>src/**/*.java</include>
+                                               <include>src/**/*.xml</include>
+                                       </includes>
+                                       <excludes>
+                                               <exclude>**/springutil/**</exclude>
+                                               <exclude>**/qooxdoo-sdk/**</exclude>
+                                       </excludes>
+                                       <useDefaultExcludes>true</useDefaultExcludes>
+                                       <mapping>
+                                               <java>SLASHSTAR_STYLE</java>
+                                               <xml>XML_STYLE</xml>
+                                       </mapping>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>com.agilejava.docbkx</groupId>
+                               <artifactId>docbkx-maven-plugin</artifactId>
+                               <configuration>
+                                       <sourceDirectory>doc/reference</sourceDirectory>
+                                       <targetDirectory>target/site/reference</targetDirectory>
+                                       <htmlStylesheet>css/style.css</htmlStylesheet>
+                                       <postProcess>
+                                               <copy todir="target/site/reference">
+                                                       <fileset dir="doc/reference">
+                                                               <include name="*/*.css" />
+                                                       </fileset>
+                                                       <fileset dir="doc/reference">
+                                                               <include name="img/**" />
+                                                       </fileset>
+                                               </copy>
+                                       </postProcess>
+                               </configuration>
+                               <executions>
+                                       <execution>
+                                               <goals>
+                                                       <goal>generate-html</goal>
+                                                       <goal>generate-pdf</goal>
+                                               </goals>
+                                               <phase>pre-site</phase>
+                                       </execution>
+                               </executions>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencyManagement>
+               <dependencies>
+                       <dependency>
+                               <groupId>org.argeo.tp</groupId>
+                               <artifactId>binaries</artifactId>
+                               <version>${version.argeo-distribution}</version>
+                               <type>pom</type>
+                               <scope>import</scope>
+                       </dependency>
+                       <!-- Eclipse RAP -->
+                       <dependency>
+                               <groupId>org.argeo.tp.rap</groupId>
+                               <artifactId>binaries</artifactId>
+                               <version>${version.argeo-distribution}</version>
+                               <type>pom</type>
+                               <scope>import</scope>
+                       </dependency>
+                       <dependency>
+                               <groupId>org.argeo.tp.rap.addons</groupId>
+                               <artifactId>binaries</artifactId>
+                               <version>${version.rap.addons}</version>
+                               <type>pom</type>
+                               <scope>import</scope>
+                       </dependency>
+                       <dependency>
+                               <groupId>org.argeo.tp.rap.platform</groupId>
+                               <artifactId>binaries</artifactId>
+                               <version>${version.rap}</version>
+                               <type>pom</type>
+                               <scope>import</scope>
+                       </dependency>
+               </dependencies>
+       </dependencyManagement>
+       <repositories>
+               <repository>
+                       <id>argeo-tp</id>
+                       <url>http://repo.argeo.org/data/public/java/argeo-tp-1.4</url>
+                       <releases>
+                               <enabled>true</enabled>
+                               <updatePolicy>daily</updatePolicy>
+                               <checksumPolicy>warn</checksumPolicy>
+                       </releases>
+               </repository>
+               <repository>
+                       <id>argeo-tp-rap</id>
+                       <url>http://repo.argeo.org/data/public/java/eclipse-rap-2.2</url>
+                       <releases>
+                               <enabled>true</enabled>
+                               <updatePolicy>daily</updatePolicy>
+                               <checksumPolicy>warn</checksumPolicy>
+                       </releases>
+               </repository>
+               <!--<repository> <id>argeo-tp-rap</id> <url>http://localhost:9070/data/public/java/eclipse-rap-2.2</url> 
+                       <releases> <enabled>true</enabled> <updatePolicy>daily</updatePolicy> <checksumPolicy>warn</checksumPolicy> 
+                       </releases> </repository> -->
+       </repositories>
+       <pluginRepositories>
+               <pluginRepository>
+                       <id>central</id>
+                       <url>http://repo1.maven.org/maven2</url>
+                       <releases>
+                               <enabled>true</enabled>
+                               <updatePolicy>daily</updatePolicy>
+                               <checksumPolicy>warn</checksumPolicy>
+                       </releases>
+               </pluginRepository>
+               <pluginRepository>
+                       <id>argeo-maven-plugins</id>
+                       <url>http://repo.argeo.org/data/public/java/argeo-maven-1.1</url>
+                       <releases>
+                               <enabled>true</enabled>
+                               <updatePolicy>daily</updatePolicy>
+                               <checksumPolicy>warn</checksumPolicy>
+                       </releases>
+               </pluginRepository>
+       </pluginRepositories>
+       <distributionManagement>
+               <repository>
+                       <id>staging</id>
+                       <url>dav:https://repo.argeo.org/data/files/java/argeo-commons-${developmentCycle.argeo-commons}</url>
+                       <uniqueVersion>false</uniqueVersion>
+               </repository>
+               <site>
+                       <id>site</id>
+                       <url>dav:https://repo.argeo.org/data/files/docs/argeo-commons-${developmentCycle.argeo-commons}</url>
+               </site>
+       </distributionManagement>
+       <profiles>
+               <profile>
+                       <id>localrepo</id>
+                       <distributionManagement>
+                               <repository>
+                                       <id>localrepo</id>
+                                       <name>Internal Release Repository</name>
+                                       <url>dav:http://localhost:7070/data/files/java/argeo-commons-${developmentCycle.argeo-commons}</url>
+                               </repository>
+                               <site>
+                                       <id>site</id>
+                                       <name>Argeo Commons Site</name>
+                                       <url>dav:http://localhost:7070/data/files/docs/argeo-commons-${developmentCycle.argeo-commons}</url>
+                               </site>
+                       </distributionManagement>
+               </profile>
+               <profile>
+                       <id>el5</id>
+                       <properties>
+                               <rpm.release>5</rpm.release>
+                               <rpm.stagingRepository>/srv/rpmfactory/argeo-osgi-staging/5/x86_64</rpm.stagingRepository>
+                       </properties>
+               </profile>
+       </profiles>
+</project>
diff --git a/trunk/security/dep/org.argeo.security.dep.node.eclipse/p2.inf b/trunk/security/dep/org.argeo.security.dep.node.eclipse/p2.inf
new file mode 100644 (file)
index 0000000..0423aa5
--- /dev/null
@@ -0,0 +1,2 @@
+properties.1.name=org.eclipse.equinox.p2.type.category
+properties.1.value=true
\ No newline at end of file
diff --git a/trunk/security/dep/org.argeo.security.dep.node.eclipse/pom.xml b/trunk/security/dep/org.argeo.security.dep.node.eclipse/pom.xml
new file mode 100644 (file)
index 0000000..464c6be
--- /dev/null
@@ -0,0 +1,137 @@
+<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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>dep</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.dep.node.eclipse</artifactId>
+       <name>Node Eclipse Perspectives</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.argeo.maven.plugins</groupId>
+                               <artifactId>maven-argeo-osgi-plugin</artifactId>
+                               <executions>
+                                       <execution>
+                                               <id>generate-descriptors</id>
+                                               <goals>
+                                                       <goal>descriptors</goal>
+                                               </goals>
+                                               <phase>generate-resources</phase>
+                                       </execution>
+                               </executions>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- Applications -->
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.jcr.ui.explorer</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.osgi.ui.explorer</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.ui.admin</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Eclipse -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui.jcr</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.equinox</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.ui</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Basis -->
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.dep.node</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+               </dependency>
+       </dependencies>
+       <profiles>
+               <profile>
+                       <id>rpmbuild</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.apache.maven.plugins</groupId>
+                                               <artifactId>maven-dependency-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>copy-direct</id>
+                                                               <phase>package</phase>
+                                                               <goals>
+                                                                       <goal>copy-dependencies</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <includeTypes>jar</includeTypes>
+                                                                       <excludeTransitive>true</excludeTransitive>
+                                                                       <outputDirectory>${project.build.directory}/argeo-node-eclipse</outputDirectory>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                                       <plugin>
+                                               <groupId>org.codehaus.mojo</groupId>
+                                               <artifactId>rpm-maven-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>rpm-node-eclipse</id>
+                                                               <phase>package</phase>
+                                                               <goals>
+                                                                       <goal>rpm</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <name>argeo-node-eclipse</name>
+                                                                       <classifier>rpm-eclipse</classifier>
+                                                                       <mappings>
+                                                                               <mapping>
+                                                                                       <directory>/usr/share/osgi/eclipse</directory>
+                                                                                       <username>root</username>
+                                                                                       <groupname>root</groupname>
+                                                                                       <filemode>644</filemode>
+                                                                                       <directoryIncluded>false</directoryIncluded>
+                                                                                       <sources>
+                                                                                               <source>
+                                                                                                       <location>${project.build.directory}/argeo-node-eclipse</location>
+                                                                                               </source>
+                                                                                       </sources>
+                                                                               </mapping>
+                                                                       </mappings>
+                                                                       <requires>
+                                                                               <require>argeo-node</require>
+                                                                       </requires>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+               </profile>
+       </profiles>
+</project>
diff --git a/trunk/security/dep/org.argeo.security.dep.node.rap/p2.inf b/trunk/security/dep/org.argeo.security.dep.node.rap/p2.inf
new file mode 100644 (file)
index 0000000..0423aa5
--- /dev/null
@@ -0,0 +1,2 @@
+properties.1.name=org.eclipse.equinox.p2.type.category
+properties.1.value=true
\ No newline at end of file
diff --git a/trunk/security/dep/org.argeo.security.dep.node.rap/pom.xml b/trunk/security/dep/org.argeo.security.dep.node.rap/pom.xml
new file mode 100644 (file)
index 0000000..efee602
--- /dev/null
@@ -0,0 +1,198 @@
+<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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>dep</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.dep.node.rap</artifactId>
+       <name>Node Eclipse RAP</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <SLC-ModularDistribution>default</SLC-ModularDistribution>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.argeo.maven.plugins</groupId>
+                               <artifactId>maven-argeo-osgi-plugin</artifactId>
+                               <executions>
+                                       <execution>
+                                               <id>generate-descriptors</id>
+                                               <goals>
+                                                       <goal>descriptors</goal>
+                                               </goals>
+                                               <phase>generate-resources</phase>
+                                       </execution>
+                               </executions>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- Applications -->
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.jcr.ui.explorer</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.osgi.ui.explorer</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.ui.admin</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.mvc</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Basis -->
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.dep.node</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+                       <exclusions>
+                               <exclusion>
+                                       <groupId>org.argeo.commons.security</groupId>
+                                       <artifactId>org.argeo.security.dao.jackrabbit</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.commons.security</groupId>
+                                       <artifactId>org.argeo.security.dao.os</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.commons.server</groupId>
+                                       <artifactId>org.argeo.server.catalina.start</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.commons.server</groupId>
+                                       <artifactId>org.argeo.server.webextender</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp</groupId>
+                                       <artifactId>org.apache.jasper</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>org.argeo.tp</groupId>
+                                       <artifactId>org.springframework.osgi.web.extender</artifactId>
+                               </exclusion>
+                       </exclusions>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.ui.rap</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- For Tomcat deployment
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.rap.webapp</artifactId>
+                       <version>2.1.1.tp-SNAPSHOT</version>
+               </dependency>
+                -->
+       </dependencies>
+       <profiles>
+               <profile>
+                       <id>check-osgi</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.argeo.maven.plugins</groupId>
+                                               <artifactId>maven-argeo-osgi-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>check-osgi</id>
+                                                               <phase>test</phase>
+                                                               <goals>
+                                                                       <goal>equinox</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <onlyCheck>true</onlyCheck>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+                       <dependencies>
+                               <!-- OSGi test -->
+                               <dependency>
+                                       <groupId>org.argeo.commons.base</groupId>
+                                       <artifactId>org.argeo.osgi.boot</artifactId>
+                                       <version>2.1.11</version>
+                                       <scope>test</scope>
+                               </dependency>
+                       </dependencies>
+               </profile>
+               <profile>
+                       <id>rpmbuild</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.codehaus.mojo</groupId>
+                                               <artifactId>rpm-maven-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>rpm-node-rap</id>
+                                                               <phase>package</phase>
+                                                               <goals>
+                                                                       <goal>rpm</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <name>argeo-node-rap</name>
+                                                                       <mappings>
+                                                                               <!-- <mapping> -->
+                                                                               <!-- <directory>/etc/argeo-node-rap</directory> -->
+                                                                               <!-- <username>root</username> -->
+                                                                               <!-- <groupname>root</groupname> -->
+                                                                               <!-- <filemode>644</filemode> -->
+                                                                               <!-- <configuration>true</configuration> -->
+                                                                               <!-- <directoryIncluded>false</directoryIncluded> -->
+                                                                               <!-- <sources> -->
+                                                                               <!-- <source> -->
+                                                                               <!-- <location>src/main/rpm/etc/argeo-node-rap</location> -->
+                                                                               <!-- </source> -->
+                                                                               <!-- </sources> -->
+                                                                               <!-- </mapping> -->
+                                                                               <mapping>
+                                                                                       <directory>/usr/share/osgi/rap</directory>
+                                                                                       <username>root</username>
+                                                                                       <groupname>root</groupname>
+                                                                                       <filemode>644</filemode>
+                                                                                       <directoryIncluded>false</directoryIncluded>
+                                                                                       <dependency>
+                                                                                               <includes>
+                                                                                                       <include>org.argeo.commons.base:org.argeo.eclipse.ui.rap</include>
+                                                                                                       <include>org.argeo.commons.server:org.argeo.server.rap.webapp</include>
+                                                                                                       <include>org.argeo.commons.security:org.argeo.security.ui.rap</include>
+                                                                                               </includes>
+                                                                                       </dependency>
+                                                                               </mapping>
+                                                                       </mappings>
+                                                                       <requires>
+                                                                               <require>argeo-node</require>
+                                                                               <require>argeo-node-eclipse</require>
+                                                                               <require>eclipse-rap</require>
+                                                                       </requires>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+               </profile>
+       </profiles>
+</project>
diff --git a/trunk/security/dep/org.argeo.security.dep.node.rap/src/assembly/dist.xml b/trunk/security/dep/org.argeo.security.dep.node.rap/src/assembly/dist.xml
new file mode 100644 (file)
index 0000000..2cbbd38
--- /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.
+
+-->
+<assembly
+       xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+       <id>dist</id>
+       <baseDirectory>argeo-node-server</baseDirectory>
+       <formats>
+               <format>tar.gz</format>
+       </formats>
+       <dependencySets>
+               <dependencySet>
+                       <unpack>false</unpack>
+                       <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}
+                       </outputFileNameMapping>
+                       <outputDirectory>lib</outputDirectory>
+                       <includes>
+                               <include>*:jar</include>
+                       </includes>
+               </dependencySet>
+       </dependencySets>
+</assembly>
\ No newline at end of file
diff --git a/trunk/security/dep/org.argeo.security.dep.node.rcp/p2.inf b/trunk/security/dep/org.argeo.security.dep.node.rcp/p2.inf
new file mode 100644 (file)
index 0000000..0423aa5
--- /dev/null
@@ -0,0 +1,2 @@
+properties.1.name=org.eclipse.equinox.p2.type.category
+properties.1.value=true
\ No newline at end of file
diff --git a/trunk/security/dep/org.argeo.security.dep.node.rcp/pom.xml b/trunk/security/dep/org.argeo.security.dep.node.rcp/pom.xml
new file mode 100644 (file)
index 0000000..4af193d
--- /dev/null
@@ -0,0 +1,129 @@
+<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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>dep</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.dep.node.rcp</artifactId>
+       <name>Node Eclipse RCP</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <SLC-ModularDistribution>default</SLC-ModularDistribution>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.argeo.maven.plugins</groupId>
+                               <artifactId>maven-argeo-osgi-plugin</artifactId>
+                               <executions>
+                                       <execution>
+                                               <id>generate-descriptors</id>
+                                               <goals>
+                                                       <goal>descriptors</goal>
+                                               </goals>
+                                               <phase>generate-resources</phase>
+                                       </execution>
+                               </executions>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.dep.node.eclipse</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.ui.rcp</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+       </dependencies>
+       <profiles>
+               <profile>
+                       <id>check-osgi</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.argeo.maven.plugins</groupId>
+                                               <artifactId>maven-argeo-osgi-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>check-osgi</id>
+                                                               <phase>test</phase>
+                                                               <goals>
+                                                                       <goal>equinox</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <onlyCheck>true</onlyCheck>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+                       <dependencies>
+                               <!-- OSGi test -->
+                               <dependency>
+                                       <groupId>org.argeo.commons.base</groupId>
+                                       <artifactId>org.argeo.osgi.boot</artifactId>
+                                       <version>2.1.11</version>
+                                       <scope>test</scope>
+                               </dependency>
+                       </dependencies>
+               </profile>
+               <profile>
+                       <id>rpmbuild</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.codehaus.mojo</groupId>
+                                               <artifactId>rpm-maven-plugin</artifactId>
+                                               <configuration>
+                                               </configuration>
+                                               <executions>
+                                                       <execution>
+                                                               <id>rpm-node-rcp</id>
+                                                               <phase>package</phase>
+                                                               <goals>
+                                                                       <goal>rpm</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <name>argeo-node-rcp</name>
+                                                                       <mappings>
+                                                                               <mapping>
+                                                                                       <directory>/usr/share/osgi/rcp</directory>
+                                                                                       <username>root</username>
+                                                                                       <groupname>root</groupname>
+                                                                                       <filemode>644</filemode>
+                                                                                       <directoryIncluded>false</directoryIncluded>
+                                                                                       <dependency>
+                                                                                               <includes>
+                                                                                                       <include>org.argeo.commons.base:org.argeo.eclipse.ui.rcp</include>
+                                                                                                       <include>org.argeo.commons.security:org.argeo.security.ui.rcp</include>
+                                                                                               </includes>
+                                                                                       </dependency>
+                                                                               </mapping>
+                                                                       </mappings>
+                                                                       <requires>
+                                                                               <require>argeo-node</require>
+                                                                               <require>argeo-node-eclipse</require>
+                                                                               <require>eclipse-platform</require>
+                                                                       </requires>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+               </profile>
+       </profiles>
+</project>
diff --git a/trunk/security/dep/org.argeo.security.dep.node.rcp/src/assembly/linux.x86.xml b/trunk/security/dep/org.argeo.security.dep.node.rcp/src/assembly/linux.x86.xml
new file mode 100644 (file)
index 0000000..0b321cd
--- /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.
+
+-->
+<!-- Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org> 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. -->
+
+<assembly
+       xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+       <id>linux.x86</id>
+       <baseDirectory>argeo-node-ui</baseDirectory>
+       <formats>
+               <format>tar.gz</format>
+       </formats>
+       <dependencySets>
+               <dependencySet>
+                       <unpack>false</unpack>
+                       <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}
+                       </outputFileNameMapping>
+                       <outputDirectory>lib</outputDirectory>
+                       <includes>
+                               <include>*:jar</include>
+                       </includes>
+                       <excludes>
+                               <exclude>org.eclipse.swt:org.eclipse.swt*:jar</exclude>
+                       </excludes>
+               </dependencySet>
+               <dependencySet>
+                       <unpack>false</unpack>
+                       <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}
+                       </outputFileNameMapping>
+                       <outputDirectory>lib</outputDirectory>
+                       <includes>
+                               <include>org.eclipse.swt:org.eclipse.swt.gtk.linux.x86:jar</include>
+                       </includes>
+               </dependencySet>
+       </dependencySets>
+</assembly>
\ No newline at end of file
diff --git a/trunk/security/dep/org.argeo.security.dep.node.rcp/src/assembly/linux.x86_64.xml b/trunk/security/dep/org.argeo.security.dep.node.rcp/src/assembly/linux.x86_64.xml
new file mode 100644 (file)
index 0000000..12a0a32
--- /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.
+
+-->
+<!-- Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org> 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. -->
+
+<assembly
+       xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+       <id>linux.x86_64</id>
+       <baseDirectory>argeo-node-ui</baseDirectory>
+       <formats>
+               <format>tar.gz</format>
+       </formats>
+       <dependencySets>
+               <dependencySet>
+                       <unpack>false</unpack>
+                       <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}
+                       </outputFileNameMapping>
+                       <outputDirectory>lib</outputDirectory>
+                       <includes>
+                               <include>*:jar</include>
+                       </includes>
+                       <excludes>
+                               <exclude>org.eclipse.swt:org.eclipse.swt*:jar</exclude>
+                       </excludes>
+               </dependencySet>
+               <dependencySet>
+                       <unpack>false</unpack>
+                       <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}
+                       </outputFileNameMapping>
+                       <outputDirectory>lib</outputDirectory>
+                       <includes>
+                               <include>org.eclipse.swt:org.eclipse.swt.gtk.linux.x86_64:jar</include>
+                       </includes>
+               </dependencySet>
+       </dependencySets>
+</assembly>
\ No newline at end of file
diff --git a/trunk/security/dep/org.argeo.security.dep.node.rcp/src/assembly/win32.x86.xml b/trunk/security/dep/org.argeo.security.dep.node.rcp/src/assembly/win32.x86.xml
new file mode 100644 (file)
index 0000000..15cec0d
--- /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.
+
+-->
+<!-- Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org> 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. -->
+
+<assembly
+       xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+       <id>win32.x86</id>
+       <baseDirectory>argeo-node-ui</baseDirectory>
+       <formats>
+               <format>zip</format>
+       </formats>
+       <dependencySets>
+               <dependencySet>
+                       <unpack>false</unpack>
+                       <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}
+                       </outputFileNameMapping>
+                       <outputDirectory>lib</outputDirectory>
+                       <includes>
+                               <include>*:jar</include>
+                       </includes>
+                       <excludes>
+                               <exclude>org.eclipse.swt:org.eclipse.swt*:jar</exclude>
+                       </excludes>
+               </dependencySet>
+               <dependencySet>
+                       <unpack>false</unpack>
+                       <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}
+                       </outputFileNameMapping>
+                       <outputDirectory>lib</outputDirectory>
+                       <includes>
+                               <include>org.eclipse.swt:org.eclipse.swt.win32.win32.x86:jar</include>
+                       </includes>
+               </dependencySet>
+       </dependencySets>
+</assembly>
\ No newline at end of file
diff --git a/trunk/security/dep/org.argeo.security.dep.node/p2.inf b/trunk/security/dep/org.argeo.security.dep.node/p2.inf
new file mode 100644 (file)
index 0000000..0423aa5
--- /dev/null
@@ -0,0 +1,2 @@
+properties.1.name=org.eclipse.equinox.p2.type.category
+properties.1.value=true
\ No newline at end of file
diff --git a/trunk/security/dep/org.argeo.security.dep.node/pom.xml b/trunk/security/dep/org.argeo.security.dep.node/pom.xml
new file mode 100644 (file)
index 0000000..0e3e38d
--- /dev/null
@@ -0,0 +1,415 @@
+<?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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>dep</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.dep.node</artifactId>
+       <name>Node Backend</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <SLC-ModularDistribution>default</SLC-ModularDistribution>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.argeo.maven.plugins</groupId>
+                               <artifactId>maven-argeo-osgi-plugin</artifactId>
+                               <executions>
+                                       <execution>
+                                               <id>generate-descriptors</id>
+                                               <goals>
+                                                       <goal>descriptors</goal>
+                                               </goals>
+                                               <phase>generate-resources</phase>
+                                       </execution>
+                               </executions>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- Default JCR repositories configurations -->
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.node.repo.jackrabbit</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- OSGi Boot (and Equinox) -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.osgi.boot</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.log4j</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+               </dependency>
+
+               <!-- Argeo Server -->
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.core</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Argeo Security -->
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.core</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Xerces and Xalan -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.xmlcommons</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.xalan</artifactId>
+               </dependency>
+
+               <!-- Javax -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.annotation</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.mail</artifactId>
+               </dependency>
+
+               <!-- Bouncycastle (cryptography) -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>bcprov</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>bcmail</artifactId>
+               </dependency>
+
+               <!-- Commons -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.vfs</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.exec</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.cli</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.dbcp</artifactId>
+               </dependency>
+
+               <!-- Reporting -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>jxl</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.pdfbox</artifactId>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.context.support</artifactId>
+               </dependency>
+
+               <!-- Spring OSGi -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.osgi.extender</artifactId>
+               </dependency>
+
+               <!-- Jackrabbit -->
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.jackrabbit</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.jackrabbit</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Scheduling -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.quartz</artifactId>
+               </dependency>
+
+               <!-- Security (LDAP) -->
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.dao.ldap</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.auth.ldap</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.ldap</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Security (Jackrabbit) -->
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.dao.jackrabbit</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Security (Standalone) -->
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.dao.os</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Web -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.osgi.web.extender</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.osgi.web</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.dep.tomcat</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.webextender</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.jcr.mvc</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.jackrabbit.webapp</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- DB drivers -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.h2</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>com.mysql.jdbc</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.postgresql.jdbc3</artifactId>
+               </dependency>
+
+               <!-- LDAP Apache Directory server -->
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.dep.ads</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.ads.server</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.ads</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Software development -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.support.junit</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+       </dependencies>
+       <profiles>
+               <profile>
+                       <id>check-osgi</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.argeo.maven.plugins</groupId>
+                                               <artifactId>maven-argeo-osgi-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>check-osgi</id>
+                                                               <phase>test</phase>
+                                                               <goals>
+                                                                       <goal>equinox</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <onlyCheck>true</onlyCheck>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+                       <dependencies>
+                               <!-- OSGi test -->
+                               <dependency>
+                                       <groupId>org.argeo.commons.base</groupId>
+                                       <artifactId>org.argeo.osgi.boot</artifactId>
+                                       <version>2.1.11</version>
+                                       <scope>test</scope>
+                               </dependency>
+                       </dependencies>
+               </profile>
+               <profile>
+                       <id>rpmbuild</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.apache.maven.plugins</groupId>
+                                               <artifactId>maven-dependency-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>copy-node</id>
+                                                               <phase>package</phase>
+                                                               <goals>
+                                                                       <goal>copy-dependencies</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <includeTypes>jar</includeTypes>
+                                                                       <includeGroupIds>org.argeo.commons.base,org.argeo.commons.server,org.argeo.commons.security</includeGroupIds>
+                                                                       <excludeArtifactIds>org.argeo.osgi.boot</excludeArtifactIds>
+                                                                       <outputDirectory>${project.build.directory}/node</outputDirectory>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                                       <plugin>
+                                               <groupId>org.codehaus.mojo</groupId>
+                                               <artifactId>rpm-maven-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>rpm-node</id>
+                                                               <phase>package</phase>
+                                                               <goals>
+                                                                       <goal>rpm</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <name>argeo-node</name>
+                                                                       <mappings>
+                                                                               <mapping>
+                                                                                       <directory>/usr/share/osgi</directory>
+                                                                                       <username>root</username>
+                                                                                       <groupname>root</groupname>
+                                                                                       <filemode>644</filemode>
+                                                                                       <directoryIncluded>false</directoryIncluded>
+                                                                                       <sources>
+                                                                                               <source>
+                                                                                                       <location>${project.build.directory}/node</location>
+                                                                                               </source>
+                                                                                       </sources>
+                                                                               </mapping>
+                                                                       </mappings>
+                                                                       <requires>
+                                                                               <require>argeo-node-tp</require>
+                                                                       </requires>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+               </profile>
+               <profile>
+                       <id>rpmbuild-tp</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.apache.maven.plugins</groupId>
+                                               <artifactId>maven-dependency-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>copy-tp</id>
+                                                               <phase>package</phase>
+                                                               <goals>
+                                                                       <goal>copy-dependencies</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <includeTypes>jar</includeTypes>
+                                                                       <excludeGroupIds>org.argeo.commons.base,org.argeo.commons.server,org.argeo.commons.security</excludeGroupIds>
+                                                                       <excludeArtifactIds>org.eclipse.osgi</excludeArtifactIds>
+                                                                       <outputDirectory>${project.build.directory}/node-tp</outputDirectory>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                                       <plugin>
+                                               <groupId>org.codehaus.mojo</groupId>
+                                               <artifactId>rpm-maven-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>rpm-node-tp</id>
+                                                               <phase>package</phase>
+                                                               <goals>
+                                                                       <goal>rpm</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <name>argeo-node-tp</name>
+                                                                       <classifier>rpm-tp</classifier>
+                                                                       <projversion>${version.argeo-distribution}</projversion>
+                                                                       <mappings>
+                                                                               <mapping>
+                                                                                       <directory>/usr/share/osgi</directory>
+                                                                                       <username>root</username>
+                                                                                       <groupname>root</groupname>
+                                                                                       <filemode>644</filemode>
+                                                                                       <directoryIncluded>false</directoryIncluded>
+                                                                                       <sources>
+                                                                                               <source>
+                                                                                                       <location>${project.build.directory}/node-tp</location>
+                                                                                               </source>
+                                                                                       </sources>
+                                                                               </mapping>
+                                                                       </mappings>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+               </profile>
+       </profiles>
+</project>
\ No newline at end of file
diff --git a/trunk/security/dep/pom.xml b/trunk/security/dep/pom.xml
new file mode 100644 (file)
index 0000000..8d96939
--- /dev/null
@@ -0,0 +1,56 @@
+<?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">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <version>2.1.11</version>
+               <artifactId>security</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <groupId>org.argeo.commons.security</groupId>
+       <artifactId>dep</artifactId>
+       <name>Commons Security Features</name>
+       <packaging>pom</packaging>
+       <modules>
+               <module>org.argeo.security.dep.node</module>
+               <module>org.argeo.security.dep.node.eclipse</module>
+               <module>org.argeo.security.dep.node.rap</module>
+               <module>org.argeo.security.dep.node.rcp</module>
+       </modules>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                       </plugin>
+               </plugins>
+       </build>
+       <profiles>
+               <profile>
+                       <id>rpmbuild</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <artifactId>maven-antrun-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <phase>install</phase>
+                                                               <goals>
+                                                                       <goal>run</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <target>
+                                                                               <copy todir="${rpm.stagingRepository}" verbose="true" failonerror="false">
+                                                                                       <fileset dir="${project.build.directory}/rpm" includes="*/RPMS/**/*.rpm" />
+                                                                                       <flattenmapper />
+                                                                               </copy>
+                                                                       </target>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+               </profile>
+       </profiles>
+</project>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.auth.ldap/.project b/trunk/security/modules/org.argeo.security.auth.ldap/.project
new file mode 100644 (file)
index 0000000..0c72d59
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.security.auth.ldap</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/security/modules/org.argeo.security.auth.ldap/META-INF/spring/security-ldap-jcr.xml b/trunk/security/modules/org.argeo.security.auth.ldap/META-INF/spring/security-ldap-jcr.xml
new file mode 100644 (file)
index 0000000..3235e66
--- /dev/null
@@ -0,0 +1,79 @@
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:security="http://www.springframework.org/schema/security"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+               http://www.springframework.org/schema/security
+               http://www.springframework.org/schema/security/spring-security-2.0.4.xsd
+               http://www.springframework.org/schema/util
+               http://www.springframework.org/schema/util/spring-util-2.5.xsd">
+
+       <bean id="argeoDataModel" class="org.argeo.jackrabbit.JackrabbitWrapper"
+               init-method="init" destroy-method="destroy">
+               <description><![CDATA[Make sure that Argeo base data model is registered]]></description>
+               <property name="cndFiles">
+                       <list>
+                               <value>/org/argeo/jcr/argeo.cnd</value>
+                       </list>
+               </property>
+               <property name="repository" ref="nodeRepository" />
+               <property name="bundleContext" ref="bundleContext" />
+       </bean>
+
+       <bean id="jcrLdapSynchronizer" class="org.argeo.security.ldap.jcr.JcrLdapSynchronizer"
+               init-method="init" destroy-method="destroy" depends-on="argeoDataModel">
+               <!-- LDAP -->
+               <property name="usernameAttribute" value="${argeo.ldap.usernameAttribute}" />
+               <property name="passwordAttribute" value="${argeo.ldap.passwordAttribute}" />
+               <property name="userClasses">
+                       <list>
+                               <value>${argeo.ldap.userClass}</value>
+                       </list>
+               </property>
+               <property name="passwordEncoder" ref="passwordEncoder" />
+               <property name="userBase" value="${argeo.ldap.userBase}" />
+               <property name="usernameMapper" ref="usernameMapper" />
+               <property name="ldapTemplate" ref="ldapTemplate" />
+               <property name="rawLdapTemplate" ref="rawLdapTemplate" />
+               <!-- JCR -->
+               <property name="repository" ref="nodeRepository" />
+               <property name="jcrSecurityModel" ref="jcrSecurityModel" />
+               <property name="propertyToAttributes" ref="propertyToAttributes" />
+       </bean>
+
+       <bean name="jcrSecurityModel" class="org.argeo.security.jackrabbit.JackrabbitSecurityModel" />
+
+       <!-- LDAP / JCR mapping -->
+       <util:map id="propertyToAttributes">
+               <entry value="cn">
+                       <key>
+                               <util:constant static-field="javax.jcr.Property.JCR_TITLE" />
+                       </key>
+               </entry>
+               <entry value="description">
+                       <key>
+                               <util:constant static-field="javax.jcr.Property.JCR_DESCRIPTION" />
+                       </key>
+               </entry>
+               <entry value="givenName">
+                       <key>
+                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_FIRST_NAME" />
+                       </key>
+               </entry>
+               <entry value="sn">
+                       <key>
+                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_LAST_NAME" />
+                       </key>
+               </entry>
+               <entry value="mail">
+                       <key>
+                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_PRIMARY_EMAIL" />
+                       </key>
+               </entry>
+               <entry value="o">
+                       <key>
+                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_PRIMARY_ORGANIZATION" />
+                       </key>
+               </entry>
+       </util:map>
+</beans>
diff --git a/trunk/security/modules/org.argeo.security.auth.ldap/META-INF/spring/security-ldap-osgi.xml b/trunk/security/modules/org.argeo.security.auth.ldap/META-INF/spring/security-ldap-osgi.xml
new file mode 100644 (file)
index 0000000..d817f96
--- /dev/null
@@ -0,0 +1,27 @@
+<?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
+       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
+\r
+       <!-- REFERENCES -->\r
+       <reference id="nodeRepository" interface="javax.jcr.Repository"\r
+               filter="(argeo.jcr.repository.alias=node)" />\r
+\r
+       <!-- SERVICES -->\r
+       <service ref="authenticationManager"\r
+               interface="org.springframework.security.AuthenticationManager"\r
+               context-class-loader="service-provider" />\r
+\r
+       <!-- User management -->\r
+<!--   <service ref="userDetailsManager" -->\r
+<!--           interface="org.springframework.security.userdetails.UserDetailsService" -->\r
+<!--           context-class-loader="service-provider" /> -->\r
+<!--   <service ref="userDetailsManager" -->\r
+<!--           interface="org.springframework.security.userdetails.UserDetailsManager" -->\r
+<!--           context-class-loader="service-provider" /> -->\r
+<!--   <service ref="userDetailsManager" interface="org.argeo.security.UserAdminService" -->\r
+<!--           context-class-loader="service-provider" /> -->\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.auth.ldap/META-INF/spring/security-ldap-services.xml b/trunk/security/modules/org.argeo.security.auth.ldap/META-INF/spring/security-ldap-services.xml
new file mode 100644 (file)
index 0000000..0b9a8b8
--- /dev/null
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+       <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
+               <property name="providers">
+                       <list>
+                               <ref bean="authByAdapterProvider" />
+<!--                           <ref bean="preAuthProvider" /> -->
+                               <ref bean="anonymousAuthenticationProvider" />
+                               <ref bean="rememberMeAuthenticationProvider" />
+                               <ref bean="ldapAuthenticationProvider" />
+                       </list>
+               </property>
+       </bean>
+
+       <!-- Authentication provider -->
+       <bean id="authByAdapterProvider"
+               class="org.springframework.security.adapters.AuthByAdapterProvider">
+               <description><![CDATA[System authentication]]></description>
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+<!--   <bean id="preAuthProvider" -->
+<!--           class="org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationProvider"> -->
+<!--           <description><![CDATA[Pre-authentication]]></description> -->
+<!--           <property name="preAuthenticatedUserDetailsService"> -->
+<!--                   <bean id="userDetailsServiceWrapper" -->
+<!--                           class="org.springframework.security.userdetails.UserDetailsByNameServiceWrapper"> -->
+<!--                           <property name="userDetailsService" ref="userDetailsManager" /> -->
+<!--                   </bean> -->
+<!--           </property> -->
+<!--   </bean> -->
+
+       <bean id="anonymousAuthenticationProvider"
+               class="org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider">
+               <description><![CDATA[Anonymous authentication]]></description>
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+       <bean id="rememberMeAuthenticationProvider"
+               class="org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider">
+               <description><![CDATA[Remember me authentication]]></description>
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+       <!-- Internal authentication, used by during the general authentication 
+               initialization himself, in order to prevent the following dependency cycle: 
+               Repository.login() <= AuthenticationManager <= LdapAuthenticationProvider 
+               <= Repository.login() in init() -->
+       <bean id="internalAuthenticationManager" class="org.springframework.security.providers.ProviderManager">
+               <property name="providers">
+                       <list>
+                               <ref bean="authByAdapterProvider" />
+                       </list>
+               </property>
+       </bean>
+
+       <bean
+               class="org.argeo.security.core.AuthenticatedApplicationContextInitialization">
+               <description><![CDATA[Executes initialization with a system authentication]]></description>
+               <property name="authenticationManager" ref="internalAuthenticationManager" />
+       </bean>
+</beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.auth.ldap/META-INF/spring/security-ldap.xml b/trunk/security/modules/org.argeo.security.auth.ldap/META-INF/spring/security-ldap.xml
new file mode 100644 (file)
index 0000000..f367aba
--- /dev/null
@@ -0,0 +1,121 @@
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:security="http://www.springframework.org/schema/security"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
+              http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
+
+       <!-- COMMON -->
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:ldap.properties</value>
+               </property>
+       </bean>
+
+       <!-- AUTHENTICATION -->
+       <bean id="ldapAuthenticationProvider"
+               class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
+               <constructor-arg ref="ldapAuthenticator" />
+               <constructor-arg ref="authoritiesPopulator" />
+               <property name="userDetailsContextMapper" ref="jcrLdapSynchronizer" />
+       </bean>
+
+       <!-- PasswordComparisonAuthenticator doesn't work with SSHA -->
+<!--   <bean id="ldapAuthenticator" -->
+<!--           class="org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator"> -->
+<!--           <constructor-arg ref="contextSource" /> -->
+<!--           <property name="userDnPatterns"> -->
+<!--                   <list> -->
+<!--                           <value><![CDATA[${argeo.ldap.usernameAttribute}={0},${argeo.ldap.userBase}]]></value> -->
+<!--                   </list> -->
+<!--           </property> -->
+<!--           <property name="passwordAttributeName" value="${argeo.ldap.passwordAttribute}" /> -->
+<!--           <property name="passwordEncoder" ref="passwordEncoder" /> -->
+<!--   </bean> -->
+
+       <!-- Bind authenticator doesn't work with Apache DS 1.0 -->
+       <bean id="ldapAuthenticator"
+               class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
+               <constructor-arg ref="contextSource" />
+               <property name="userDnPatterns">
+                       <list>
+                               <value><![CDATA[${argeo.ldap.usernameAttribute}={0},${argeo.ldap.userBase}]]></value>
+                       </list>
+               </property>
+       </bean>
+
+       <!-- USER DETAILS -->
+<!--   <bean id="userDetailsManager" class="org.argeo.security.ldap.ArgeoLdapUserDetailsManager"> -->
+<!--           <constructor-arg ref="contextSource" /> -->
+<!--           <property name="groupSearchBase" value="${argeo.ldap.groupBase}" /> -->
+<!--           <property name="groupMemberAttributeName" value="${argeo.ldap.groupMemberAttribute}" /> -->
+<!--           <property name="usernameMapper" ref="usernameMapper" /> -->
+<!--           <property name="userDetailsMapper" ref="jcrLdapSynchronizer" /> -->
+<!--           <property name="userAdminDao" ref="userAdminDao" /> -->
+<!--           <property name="passwordEncoder" ref="passwordEncoder" /> -->
+<!--           <property name="passwordAttributeName" value="${argeo.ldap.passwordAttribute}" /> -->
+<!--           <property name="superUsername" value="${argeo.security.superUsername}" /> -->
+<!--   </bean> -->
+
+<!--   <bean id="userAdminDao" class="org.argeo.security.ldap.ArgeoUserAdminDaoLdap"> -->
+<!--           <constructor-arg ref="contextSource" /> -->
+<!--           <property name="userBase" value="${argeo.ldap.userBase}" /> -->
+<!--           <property name="usernameAttribute" value="${argeo.ldap.usernameAttribute}" /> -->
+<!--           <property name="groupClasses"> -->
+<!--                   <list> -->
+<!--                           <value>top</value> -->
+<!--                           <value>${argeo.ldap.groupClass}</value> -->
+<!--                   </list> -->
+<!--           </property> -->
+<!--           <property name="groupBase" value="${argeo.ldap.groupBase}" /> -->
+<!--           <property name="groupRoleAttribute" value="${argeo.ldap.groupRoleAttribute}" /> -->
+<!--           <property name="groupMemberAttribute" value="${argeo.ldap.groupMemberAttribute}" /> -->
+<!--           <property name="defaultRole" value="${argeo.security.defaultRole}" /> -->
+<!--           <property name="rolePrefix" value="${argeo.security.rolePrefix}" /> -->
+<!--           <property name="usernameMapper" ref="usernameMapper" /> -->
+<!--   </bean> -->
+
+       <bean id="usernameMapper"
+               class="org.springframework.security.ldap.DefaultLdapUsernameToDnMapper">
+               <constructor-arg value="${argeo.ldap.userBase}" />
+               <constructor-arg value="${argeo.ldap.usernameAttribute}" />
+       </bean>
+
+       <bean id="authoritiesPopulator"
+               class="org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator">
+               <constructor-arg ref="contextSource" />
+               <constructor-arg value="${argeo.ldap.groupBase}" />
+               <property name="groupSearchFilter" value="${argeo.ldap.groupMemberAttribute}={0}" />
+               <property name="defaultRole" value="${argeo.security.defaultRole}" />
+               <property name="rolePrefix" value="${argeo.security.rolePrefix}" />
+       </bean>
+
+       <!-- LDAP LOW LEVEL -->
+       <bean id="contextSource"
+               class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
+               <constructor-arg
+                       value="${argeo.ldap.protocol}://${argeo.ldap.host}:${argeo.ldap.port}/${argeo.ldap.rootdn}" />
+<!--           <property name="userDn" value="${argeo.ldap.manager.userdn}" /> -->
+<!--           <property name="password" value="${argeo.ldap.manager.password}" /> -->
+       </bean>
+
+       <bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
+               <constructor-arg ref="contextSource" />
+       </bean>
+
+       <bean id="rawLdapTemplate" class="org.springframework.ldap.core.LdapTemplate">
+               <description><![CDATA[LDAP template returning raw dir contexts, see http://forum.springsource.org/showthread.php?55955-Persistent-search-with-spring-ldap]]></description>
+               <constructor-arg>
+                       <bean parent="contextSource">
+                               <property name="dirObjectFactory">
+                                       <null />
+                               </property>
+                       </bean>
+               </constructor-arg>
+       </bean>
+
+       <bean id="passwordEncoder" class="org.argeo.security.ldap.ArgeoLdapShaPasswordEncoder">
+               <property name="useSalt" value="${argeo.ldap.password.useSalt}" />
+       </bean>
+</beans>
diff --git a/trunk/security/modules/org.argeo.security.auth.ldap/build.properties b/trunk/security/modules/org.argeo.security.auth.ldap/build.properties
new file mode 100644 (file)
index 0000000..5f22cdd
--- /dev/null
@@ -0,0 +1 @@
+bin.includes = META-INF/
diff --git a/trunk/security/modules/org.argeo.security.auth.ldap/ldap.properties b/trunk/security/modules/org.argeo.security.auth.ldap/ldap.properties
new file mode 100644 (file)
index 0000000..0f5164f
--- /dev/null
@@ -0,0 +1,32 @@
+argeo.security.defaultRole=ROLE_USER
+argeo.security.rolePrefix=ROLE_
+
+argeo.security.systemKey=argeo
+argeo.security.superUsername=root
+
+argeo.ldap.rootdn=dc=demo,dc=example,dc=org
+argeo.ldap.protocol=ldap
+argeo.ldap.host=localhost
+# default are for Apache Directory Server
+argeo.ldap.port=10389
+argeo.ldap.manager.userdn=uid=admin,ou=system
+argeo.ldap.manager.password=secret
+
+# USER
+argeo.ldap.userClass=inetOrgPerson
+argeo.ldap.osUserClass=posixAccount
+argeo.ldap.userBase=ou=People
+argeo.ldap.usernameAttribute=uid
+argeo.ldap.passwordAttribute=userPassword
+# ROLES
+argeo.ldap.groupClass=groupOfNames
+argeo.ldap.groupBase=ou=Roles
+argeo.ldap.groupRoleAttribute=cn
+argeo.ldap.groupMemberAttribute=member
+# OS GROUPS
+argeo.ldap.osGroupClass=posixGroup
+argeo.ldap.osGroupBase=ou=Group
+argeo.ldap.osGroupNameAttribute=cn
+argeo.ldap.osGroupMemberAttribute=memberUid
+
+argeo.ldap.password.useSalt=false
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.auth.ldap/pom.xml b/trunk/security/modules/org.argeo.security.auth.ldap/pom.xml
new file mode 100644 (file)
index 0000000..f3f0e7e
--- /dev/null
@@ -0,0 +1,30 @@
+<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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.auth.ldap</artifactId>
+       <name>Commons Security Auth LDAP</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Import-Package>
+                                                       *,
+                                                       org.argeo.jcr,
+                                                       com.sun.jndi.ldap;resolution:=optional,
+                                                       org.springframework.ldap.core.support,
+                                                       org.springframework.security
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.cli/.project b/trunk/security/modules/org.argeo.security.dao.cli/.project
new file mode 100644 (file)
index 0000000..807ba67
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.security.dao.cli</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/security/modules/org.argeo.security.dao.cli/META-INF/spring/security-cli-osgi.xml b/trunk/security/modules/org.argeo.security.dao.cli/META-INF/spring/security-cli-osgi.xml
new file mode 100644 (file)
index 0000000..c46f276
--- /dev/null
@@ -0,0 +1,36 @@
+<?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
+       <!-- REFERENCE -->\r
+       <reference id="nodeRepository" interface="javax.jcr.Repository"\r
+               filter="(argeo.jcr.repository.alias=node)" />\r
+\r
+       <!-- SERVICES -->\r
+       <service ref="authenticationManager"\r
+               interface="org.springframework.security.AuthenticationManager" />\r
+\r
+       <!-- User management -->\r
+       <service ref="userDetailsManager"\r
+               interface="org.springframework.security.userdetails.UserDetailsService"\r
+               context-class-loader="service-provider" />\r
+       <service ref="userDetailsManager"\r
+               interface="org.springframework.security.userdetails.UserDetailsManager"\r
+               context-class-loader="service-provider" />\r
+       <service ref="userDetailsManager" interface="org.argeo.security.UserAdminService"\r
+               context-class-loader="service-provider" />\r
+\r
+       <!-- Callback handler and keyring -->\r
+       <service interface="javax.security.auth.callback.CallbackHandler"\r
+               ref="defaultCallbackHandler" />\r
+       <service interface="org.argeo.security.crypto.CryptoKeyring"\r
+               ref="keyring" />\r
+\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.cli/META-INF/spring/security-cli.xml b/trunk/security/modules/org.argeo.security.dao.cli/META-INF/spring/security-cli.xml
new file mode 100644 (file)
index 0000000..c3f6c81
--- /dev/null
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:security.properties</value>
+               </property>
+       </bean>
+
+       <bean id="argeoDataModel" class="org.argeo.jackrabbit.JackrabbitWrapper"
+               init-method="init" destroy-method="destroy">
+               <description><![CDATA[Make sure that Argeo base data model is registered]]></description>
+               <property name="cndFiles">
+                       <list>
+                               <value>/org/argeo/jcr/argeo.cnd</value>
+                       </list>
+               </property>
+               <property name="repository" ref="nodeRepository" />
+               <property name="bundleContext" ref="bundleContext" />
+       </bean>
+
+       <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
+               <property name="providers">
+                       <list>
+                               <ref bean="authByAdapterProvider" />
+                               <ref bean="osJcrAuthenticationProvider" />
+                       </list>
+               </property>
+       </bean>
+
+       <!-- Authentication providers -->
+       <bean id="osJcrAuthenticationProvider" class="org.argeo.security.jcr.OsJcrAuthenticationProvider"
+               init-method="init" destroy-method="destroy" depends-on="argeoDataModel">
+               <property name="repository" ref="nodeRepository" />
+               <property name="jcrSecurityModel" ref="jcrSecurityModel" />
+       </bean>
+
+       <bean name="jcrSecurityModel" class="org.argeo.security.jackrabbit.JackrabbitSecurityModel" />
+
+       <bean id="authByAdapterProvider"
+               class="org.springframework.security.adapters.AuthByAdapterProvider">
+               <description><![CDATA[System authentication]]></description>
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+       <!-- Internal authentication, used by the general authentication process 
+               himself, in order to prevent the following dependency cycle: Repository.login() 
+               <= AuthenticationManager <= osJcrAuthenticationProvider <= Repository.login() 
+               in init() -->
+       <bean id="internalAuthenticationManager" class="org.springframework.security.providers.ProviderManager">
+               <property name="providers">
+                       <list>
+                               <ref bean="authByAdapterProvider" />
+                       </list>
+               </property>
+       </bean>
+
+       <bean
+               class="org.argeo.security.core.AuthenticatedApplicationContextInitialization">
+               <description><![CDATA[Executes initialization with a system authentication]]></description>
+               <property name="authenticationManager" ref="internalAuthenticationManager" />
+       </bean>
+
+       <!-- Dummy user manager -->
+       <bean id="userDetailsManager" class="org.argeo.security.jcr.OsJcrUserAdminService"
+               init-method="init" destroy-method="destroy">
+               <property name="repository" ref="nodeRepository" />
+       </bean>
+
+       <!-- Default callback handler and keyring -->
+       <bean id="defaultCallbackHandler" class="org.argeo.security.core.ConsoleCallbackHandler" />
+
+       <bean id="nodeSession" class="org.argeo.jcr.spring.ThreadBoundSession">
+               <property name="repository" ref="nodeRepository" />
+       </bean>
+
+       <bean id="keyring" class="org.argeo.security.jcr.JcrKeyring">
+               <property name="session" ref="nodeSession" />
+               <property name="defaultCallbackHandler" ref="defaultCallbackHandler" />
+               <property name="secreteKeyLength" value="${argeo.keyring.secreteKeyLength}" />
+       </bean>
+</beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.cli/pom.xml b/trunk/security/modules/org.argeo.security.dao.cli/pom.xml
new file mode 100644 (file)
index 0000000..9486097
--- /dev/null
@@ -0,0 +1,27 @@
+<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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.dao.cli</artifactId>
+       <name>Commons Security DAO CLI</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Import-Package>
+                                                       *,
+                                                       org.argeo.jcr,
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.cli/security.properties b/trunk/security/modules/org.argeo.security.dao.cli/security.properties
new file mode 100644 (file)
index 0000000..42df925
--- /dev/null
@@ -0,0 +1,3 @@
+argeo.security.systemKey=argeo
+argeo.node.repo.securityWorkspace=security
+argeo.keyring.secreteKeyLength=256
diff --git a/trunk/security/modules/org.argeo.security.dao.jackrabbit/.project b/trunk/security/modules/org.argeo.security.dao.jackrabbit/.project
new file mode 100644 (file)
index 0000000..b537a75
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.security.dao.jackrabbit</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/repofactory.xml b/trunk/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/repofactory.xml
new file mode 100644 (file)
index 0000000..a00c9b0
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+       <bean id="repositoryFactory" class="org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory">
+               <property name="bundleContext" ref="bundleContext" />
+       </bean>
+
+</beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/security-jcr-osgi.xml b/trunk/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/security-jcr-osgi.xml
new file mode 100644 (file)
index 0000000..9f6d432
--- /dev/null
@@ -0,0 +1,28 @@
+<?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
+       <!-- SERVICES -->\r
+       <service ref="authenticationManager"\r
+               interface="org.springframework.security.AuthenticationManager" />\r
+\r
+       <service ref="repositoryFactory" interface="javax.jcr.RepositoryFactory" />\r
+\r
+       <!-- User management -->\r
+       <service ref="userDetailsManager"\r
+               interface="org.springframework.security.userdetails.UserDetailsService"\r
+               context-class-loader="service-provider" />\r
+       <service ref="userDetailsManager"\r
+               interface="org.springframework.security.userdetails.UserDetailsManager"\r
+               context-class-loader="service-provider" />\r
+       <service ref="userDetailsManager" interface="org.argeo.security.UserAdminService"\r
+               context-class-loader="service-provider" />\r
+\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/security-jcr-services.xml b/trunk/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/security-jcr-services.xml
new file mode 100644 (file)
index 0000000..1300a05
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:security.properties</value>
+               </property>
+       </bean>
+
+       <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
+               <property name="providers">
+                       <list>
+                               <ref bean="authByAdapterProvider" />
+                               <ref bean="remoteJcrAuthenticationProvider" />
+                       </list>
+               </property>
+       </bean>
+
+       <!-- Authentication providers -->
+       <bean id="remoteJcrAuthenticationProvider" class="org.argeo.security.jcr.RemoteJcrAuthenticationProvider">
+               <property name="repositoryFactory" ref="repositoryFactory" />
+               <property name="bundleContext" ref="bundleContext" />
+       </bean>
+
+       <bean id="authByAdapterProvider"
+               class="org.springframework.security.adapters.AuthByAdapterProvider">
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+       <!-- Dummy user manager -->
+       <bean id="userDetailsManager" class="org.argeo.security.jcr.OsJcrUserAdminService"
+               init-method="init" destroy-method="destroy">
+       </bean>
+
+</beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.jackrabbit/build.properties b/trunk/security/modules/org.argeo.security.dao.jackrabbit/build.properties
new file mode 100644 (file)
index 0000000..5f22cdd
--- /dev/null
@@ -0,0 +1 @@
+bin.includes = META-INF/
diff --git a/trunk/security/modules/org.argeo.security.dao.jackrabbit/pom.xml b/trunk/security/modules/org.argeo.security.dao.jackrabbit/pom.xml
new file mode 100644 (file)
index 0000000..85ae786
--- /dev/null
@@ -0,0 +1,19 @@
+<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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.dao.jackrabbit</artifactId>
+       <name>Commons Security DAO Jackrabbit</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.jackrabbit/security.properties b/trunk/security/modules/org.argeo.security.dao.jackrabbit/security.properties
new file mode 100644 (file)
index 0000000..beebcb5
--- /dev/null
@@ -0,0 +1,2 @@
+argeo.security.systemKey=argeo
+argeo.node.repo.alias=node
diff --git a/trunk/security/modules/org.argeo.security.dao.ldap/.project b/trunk/security/modules/org.argeo.security.dao.ldap/.project
new file mode 100644 (file)
index 0000000..cd8b393
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.security.dao.ldap</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-jcr.xml b/trunk/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-jcr.xml
new file mode 100644 (file)
index 0000000..3235e66
--- /dev/null
@@ -0,0 +1,79 @@
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:security="http://www.springframework.org/schema/security"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+               http://www.springframework.org/schema/security
+               http://www.springframework.org/schema/security/spring-security-2.0.4.xsd
+               http://www.springframework.org/schema/util
+               http://www.springframework.org/schema/util/spring-util-2.5.xsd">
+
+       <bean id="argeoDataModel" class="org.argeo.jackrabbit.JackrabbitWrapper"
+               init-method="init" destroy-method="destroy">
+               <description><![CDATA[Make sure that Argeo base data model is registered]]></description>
+               <property name="cndFiles">
+                       <list>
+                               <value>/org/argeo/jcr/argeo.cnd</value>
+                       </list>
+               </property>
+               <property name="repository" ref="nodeRepository" />
+               <property name="bundleContext" ref="bundleContext" />
+       </bean>
+
+       <bean id="jcrLdapSynchronizer" class="org.argeo.security.ldap.jcr.JcrLdapSynchronizer"
+               init-method="init" destroy-method="destroy" depends-on="argeoDataModel">
+               <!-- LDAP -->
+               <property name="usernameAttribute" value="${argeo.ldap.usernameAttribute}" />
+               <property name="passwordAttribute" value="${argeo.ldap.passwordAttribute}" />
+               <property name="userClasses">
+                       <list>
+                               <value>${argeo.ldap.userClass}</value>
+                       </list>
+               </property>
+               <property name="passwordEncoder" ref="passwordEncoder" />
+               <property name="userBase" value="${argeo.ldap.userBase}" />
+               <property name="usernameMapper" ref="usernameMapper" />
+               <property name="ldapTemplate" ref="ldapTemplate" />
+               <property name="rawLdapTemplate" ref="rawLdapTemplate" />
+               <!-- JCR -->
+               <property name="repository" ref="nodeRepository" />
+               <property name="jcrSecurityModel" ref="jcrSecurityModel" />
+               <property name="propertyToAttributes" ref="propertyToAttributes" />
+       </bean>
+
+       <bean name="jcrSecurityModel" class="org.argeo.security.jackrabbit.JackrabbitSecurityModel" />
+
+       <!-- LDAP / JCR mapping -->
+       <util:map id="propertyToAttributes">
+               <entry value="cn">
+                       <key>
+                               <util:constant static-field="javax.jcr.Property.JCR_TITLE" />
+                       </key>
+               </entry>
+               <entry value="description">
+                       <key>
+                               <util:constant static-field="javax.jcr.Property.JCR_DESCRIPTION" />
+                       </key>
+               </entry>
+               <entry value="givenName">
+                       <key>
+                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_FIRST_NAME" />
+                       </key>
+               </entry>
+               <entry value="sn">
+                       <key>
+                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_LAST_NAME" />
+                       </key>
+               </entry>
+               <entry value="mail">
+                       <key>
+                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_PRIMARY_EMAIL" />
+                       </key>
+               </entry>
+               <entry value="o">
+                       <key>
+                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_PRIMARY_ORGANIZATION" />
+                       </key>
+               </entry>
+       </util:map>
+</beans>
diff --git a/trunk/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-osgi.xml b/trunk/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-osgi.xml
new file mode 100644 (file)
index 0000000..aa3b67a
--- /dev/null
@@ -0,0 +1,27 @@
+<?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
+       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
+\r
+       <!-- REFERENCES -->\r
+       <reference id="nodeRepository" interface="javax.jcr.Repository"\r
+               filter="(argeo.jcr.repository.alias=node)" />\r
+\r
+       <!-- SERVICES -->\r
+       <service ref="authenticationManager"\r
+               interface="org.springframework.security.AuthenticationManager"\r
+               context-class-loader="service-provider" />\r
+\r
+       <!-- User management -->\r
+       <service ref="userDetailsManager"\r
+               interface="org.springframework.security.userdetails.UserDetailsService"\r
+               context-class-loader="service-provider" />\r
+       <service ref="userDetailsManager"\r
+               interface="org.springframework.security.userdetails.UserDetailsManager"\r
+               context-class-loader="service-provider" />\r
+       <service ref="userDetailsManager" interface="org.argeo.security.UserAdminService"\r
+               context-class-loader="service-provider" />\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-services.xml b/trunk/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-services.xml
new file mode 100644 (file)
index 0000000..36dedf3
--- /dev/null
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+       <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
+               <property name="providers">
+                       <list>
+                               <ref bean="authByAdapterProvider" />
+                               <ref bean="preAuthProvider" />
+                               <ref bean="anonymousAuthenticationProvider" />
+                               <ref bean="rememberMeAuthenticationProvider" />
+                               <ref bean="ldapAuthenticationProvider" />
+                       </list>
+               </property>
+       </bean>
+
+       <!-- Authentication provider -->
+       <bean id="authByAdapterProvider"
+               class="org.springframework.security.adapters.AuthByAdapterProvider">
+               <description><![CDATA[System authentication]]></description>
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+       <bean id="preAuthProvider"
+               class="org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationProvider">
+               <description><![CDATA[Pre-authentication]]></description>
+               <property name="preAuthenticatedUserDetailsService">
+                       <bean id="userDetailsServiceWrapper"
+                               class="org.springframework.security.userdetails.UserDetailsByNameServiceWrapper">
+                               <property name="userDetailsService" ref="userDetailsManager" />
+                       </bean>
+               </property>
+       </bean>
+
+       <bean id="anonymousAuthenticationProvider"
+               class="org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider">
+               <description><![CDATA[Anonymous authentication]]></description>
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+       <bean id="rememberMeAuthenticationProvider"
+               class="org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider">
+               <description><![CDATA[Remember me authentication]]></description>
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+       <!-- Internal authentication, used by during the general authentication 
+               initialization himself, in order to prevent the following dependency cycle: 
+               Repository.login() <= AuthenticationManager <= LdapAuthenticationProvider 
+               <= Repository.login() in init() -->
+       <bean id="internalAuthenticationManager" class="org.springframework.security.providers.ProviderManager">
+               <property name="providers">
+                       <list>
+                               <ref bean="authByAdapterProvider" />
+                       </list>
+               </property>
+       </bean>
+
+       <bean
+               class="org.argeo.security.core.AuthenticatedApplicationContextInitialization">
+               <description><![CDATA[Executes initialization with a system authentication]]></description>
+               <property name="authenticationManager" ref="internalAuthenticationManager" />
+       </bean>
+</beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap.xml b/trunk/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap.xml
new file mode 100644 (file)
index 0000000..3777f88
--- /dev/null
@@ -0,0 +1,121 @@
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:security="http://www.springframework.org/schema/security"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
+              http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
+
+       <!-- COMMON -->
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:ldap.properties</value>
+               </property>
+       </bean>
+
+       <!-- AUTHENTICATION -->
+       <bean id="ldapAuthenticationProvider"
+               class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
+               <constructor-arg ref="ldapAuthenticator" />
+               <constructor-arg ref="authoritiesPopulator" />
+               <property name="userDetailsContextMapper" ref="jcrLdapSynchronizer" />
+       </bean>
+
+       <!-- PasswordComparisonAuthenticator doesn't work with SSHA -->
+       <bean id="ldapAuthenticator"
+               class="org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator">
+               <constructor-arg ref="contextSource" />
+               <property name="userDnPatterns">
+                       <list>
+                               <value><![CDATA[${argeo.ldap.usernameAttribute}={0},${argeo.ldap.userBase}]]></value>
+                       </list>
+               </property>
+               <property name="passwordAttributeName" value="${argeo.ldap.passwordAttribute}" />
+               <property name="passwordEncoder" ref="passwordEncoder" />
+       </bean>
+
+       <!-- Bind authenticator doesn't work with Apache DS 1.0 -->
+       <!-- <bean id="ldapAuthenticator" -->
+       <!-- class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator"> -->
+       <!-- <constructor-arg ref="contextSource" /> -->
+       <!-- <property name="userDnPatterns"> -->
+       <!-- <list> -->
+       <!-- <value><![CDATA[${argeo.ldap.usernameAttribute}={0},${argeo.ldap.userBase}]]></value> -->
+       <!-- </list> -->
+       <!-- </property> -->
+       <!-- </bean> -->
+
+       <!-- USER DETAILS -->
+       <bean id="userDetailsManager" class="org.argeo.security.ldap.ArgeoLdapUserDetailsManager">
+               <constructor-arg ref="contextSource" />
+               <property name="groupSearchBase" value="${argeo.ldap.groupBase}" />
+               <property name="groupMemberAttributeName" value="${argeo.ldap.groupMemberAttribute}" />
+               <property name="usernameMapper" ref="usernameMapper" />
+               <property name="userDetailsMapper" ref="jcrLdapSynchronizer" />
+               <property name="userAdminDao" ref="userAdminDao" />
+               <property name="passwordEncoder" ref="passwordEncoder" />
+               <property name="passwordAttributeName" value="${argeo.ldap.passwordAttribute}" />
+               <property name="superUsername" value="${argeo.security.superUsername}" />
+       </bean>
+
+       <bean id="userAdminDao" class="org.argeo.security.ldap.ArgeoUserAdminDaoLdap">
+               <constructor-arg ref="contextSource" />
+               <property name="userBase" value="${argeo.ldap.userBase}" />
+               <property name="usernameAttribute" value="${argeo.ldap.usernameAttribute}" />
+               <property name="groupClasses">
+                       <list>
+                               <value>top</value>
+                               <value>${argeo.ldap.groupClass}</value>
+                       </list>
+               </property>
+               <property name="groupBase" value="${argeo.ldap.groupBase}" />
+               <property name="groupRoleAttribute" value="${argeo.ldap.groupRoleAttribute}" />
+               <property name="groupMemberAttribute" value="${argeo.ldap.groupMemberAttribute}" />
+               <property name="defaultRole" value="${argeo.security.defaultRole}" />
+               <property name="rolePrefix" value="${argeo.security.rolePrefix}" />
+               <property name="usernameMapper" ref="usernameMapper" />
+       </bean>
+
+       <bean id="usernameMapper"
+               class="org.springframework.security.ldap.DefaultLdapUsernameToDnMapper">
+               <constructor-arg value="${argeo.ldap.userBase}" />
+               <constructor-arg value="${argeo.ldap.usernameAttribute}" />
+       </bean>
+
+       <bean id="authoritiesPopulator"
+               class="org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator">
+               <constructor-arg ref="contextSource" />
+               <constructor-arg value="${argeo.ldap.groupBase}" />
+               <property name="groupSearchFilter" value="${argeo.ldap.groupMemberAttribute}={0}" />
+               <property name="defaultRole" value="${argeo.security.defaultRole}" />
+               <property name="rolePrefix" value="${argeo.security.rolePrefix}" />
+       </bean>
+
+       <!-- LDAP LOW LEVEL -->
+       <bean id="contextSource"
+               class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
+               <constructor-arg
+                       value="${argeo.ldap.protocol}://${argeo.ldap.host}:${argeo.ldap.port}/${argeo.ldap.rootdn}" />
+               <property name="userDn" value="${argeo.ldap.manager.userdn}" />
+               <property name="password" value="${argeo.ldap.manager.password}" />
+       </bean>
+
+       <bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
+               <constructor-arg ref="contextSource" />
+       </bean>
+
+       <bean id="rawLdapTemplate" class="org.springframework.ldap.core.LdapTemplate">
+               <description><![CDATA[LDAP template returning raw dir contexts, see http://forum.springsource.org/showthread.php?55955-Persistent-search-with-spring-ldap]]></description>
+               <constructor-arg>
+                       <bean parent="contextSource">
+                               <property name="dirObjectFactory">
+                                       <null />
+                               </property>
+                       </bean>
+               </constructor-arg>
+       </bean>
+
+       <bean id="passwordEncoder" class="org.argeo.security.ldap.ArgeoLdapShaPasswordEncoder">
+               <property name="useSalt" value="${argeo.ldap.password.useSalt}" />
+       </bean>
+</beans>
diff --git a/trunk/security/modules/org.argeo.security.dao.ldap/build.properties b/trunk/security/modules/org.argeo.security.dao.ldap/build.properties
new file mode 100644 (file)
index 0000000..5f22cdd
--- /dev/null
@@ -0,0 +1 @@
+bin.includes = META-INF/
diff --git a/trunk/security/modules/org.argeo.security.dao.ldap/ldap.properties b/trunk/security/modules/org.argeo.security.dao.ldap/ldap.properties
new file mode 100644 (file)
index 0000000..0f5164f
--- /dev/null
@@ -0,0 +1,32 @@
+argeo.security.defaultRole=ROLE_USER
+argeo.security.rolePrefix=ROLE_
+
+argeo.security.systemKey=argeo
+argeo.security.superUsername=root
+
+argeo.ldap.rootdn=dc=demo,dc=example,dc=org
+argeo.ldap.protocol=ldap
+argeo.ldap.host=localhost
+# default are for Apache Directory Server
+argeo.ldap.port=10389
+argeo.ldap.manager.userdn=uid=admin,ou=system
+argeo.ldap.manager.password=secret
+
+# USER
+argeo.ldap.userClass=inetOrgPerson
+argeo.ldap.osUserClass=posixAccount
+argeo.ldap.userBase=ou=People
+argeo.ldap.usernameAttribute=uid
+argeo.ldap.passwordAttribute=userPassword
+# ROLES
+argeo.ldap.groupClass=groupOfNames
+argeo.ldap.groupBase=ou=Roles
+argeo.ldap.groupRoleAttribute=cn
+argeo.ldap.groupMemberAttribute=member
+# OS GROUPS
+argeo.ldap.osGroupClass=posixGroup
+argeo.ldap.osGroupBase=ou=Group
+argeo.ldap.osGroupNameAttribute=cn
+argeo.ldap.osGroupMemberAttribute=memberUid
+
+argeo.ldap.password.useSalt=false
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.ldap/pom.xml b/trunk/security/modules/org.argeo.security.dao.ldap/pom.xml
new file mode 100644 (file)
index 0000000..9687f5c
--- /dev/null
@@ -0,0 +1,30 @@
+<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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.dao.ldap</artifactId>
+       <name>Commons Security DAO LDAP</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Import-Package>
+                                                       *,
+                                                       org.argeo.jcr,
+                                                       com.sun.jndi.ldap;resolution:=optional,
+                                                       org.springframework.ldap.core.support,
+                                                       org.springframework.security
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.os/.project b/trunk/security/modules/org.argeo.security.dao.os/.project
new file mode 100644 (file)
index 0000000..cefcc33
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.security.dao.os</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/security/modules/org.argeo.security.dao.os/META-INF/spring/security-os-osgi.xml b/trunk/security/modules/org.argeo.security.dao.os/META-INF/spring/security-os-osgi.xml
new file mode 100644 (file)
index 0000000..5d827b4
--- /dev/null
@@ -0,0 +1,30 @@
+<?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
+       <!-- REFERENCE -->\r
+       <reference id="nodeRepository" interface="javax.jcr.Repository"\r
+               filter="(argeo.jcr.repository.alias=node)" />\r
+\r
+       <!-- SERVICES -->\r
+       <service ref="authenticationManager"\r
+               interface="org.springframework.security.AuthenticationManager" />\r
+\r
+       <!-- User management -->\r
+       <service ref="userDetailsManager"\r
+               interface="org.springframework.security.userdetails.UserDetailsService"\r
+               context-class-loader="service-provider" />\r
+       <service ref="userDetailsManager"\r
+               interface="org.springframework.security.userdetails.UserDetailsManager"\r
+               context-class-loader="service-provider" />\r
+       <service ref="userDetailsManager" interface="org.argeo.security.UserAdminService"\r
+               context-class-loader="service-provider" />\r
+\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.os/META-INF/spring/security-os.xml b/trunk/security/modules/org.argeo.security.dao.os/META-INF/spring/security-os.xml
new file mode 100644 (file)
index 0000000..3d94f50
--- /dev/null
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:security.properties</value>
+               </property>
+       </bean>
+
+       <bean id="argeoDataModel" class="org.argeo.jackrabbit.JackrabbitWrapper"
+               init-method="init" destroy-method="destroy">
+               <description><![CDATA[Make sure that Argeo base data model is registered]]></description>
+               <property name="cndFiles">
+                       <list>
+                               <value>/org/argeo/jcr/argeo.cnd</value>
+                       </list>
+               </property>
+               <property name="repository" ref="nodeRepository" />
+               <property name="bundleContext" ref="bundleContext" />
+       </bean>
+
+       <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
+               <property name="providers">
+                       <list>
+                               <ref bean="authByAdapterProvider" />
+                               <ref bean="osJcrAuthenticationProvider" />
+                       </list>
+               </property>
+       </bean>
+
+       <!-- Authentication providers -->
+       <bean id="osJcrAuthenticationProvider" class="org.argeo.security.jcr.OsJcrAuthenticationProvider"
+               init-method="init" destroy-method="destroy" depends-on="argeoDataModel">
+               <property name="repository" ref="nodeRepository" />
+               <property name="jcrSecurityModel" ref="jcrSecurityModel" />
+       </bean>
+
+       <bean name="jcrSecurityModel" class="org.argeo.security.jackrabbit.JackrabbitSecurityModel" />
+
+       <bean id="authByAdapterProvider"
+               class="org.springframework.security.adapters.AuthByAdapterProvider">
+               <description><![CDATA[System authentication]]></description>
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+       <!-- Internal authentication, used by the general authentication process 
+               himself, in order to prevent the following dependency cycle: Repository.login() 
+               <= AuthenticationManager <= osJcrAuthenticationProvider <= Repository.login() 
+               in init() -->
+       <bean id="internalAuthenticationManager" class="org.springframework.security.providers.ProviderManager">
+               <property name="providers">
+                       <list>
+                               <ref bean="authByAdapterProvider" />
+                       </list>
+               </property>
+       </bean>
+
+       <bean
+               class="org.argeo.security.core.AuthenticatedApplicationContextInitialization">
+               <description><![CDATA[Executes initialization with a system authentication]]></description>
+               <property name="authenticationManager" ref="internalAuthenticationManager" />
+       </bean>
+
+       <!-- Dummy user manager -->
+       <bean id="userDetailsManager" class="org.argeo.security.jcr.OsJcrUserAdminService"
+               init-method="init" destroy-method="destroy">
+               <property name="repository" ref="nodeRepository" />
+       </bean>
+
+
+</beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.os/build.properties b/trunk/security/modules/org.argeo.security.dao.os/build.properties
new file mode 100644 (file)
index 0000000..5f22cdd
--- /dev/null
@@ -0,0 +1 @@
+bin.includes = META-INF/
diff --git a/trunk/security/modules/org.argeo.security.dao.os/pom.xml b/trunk/security/modules/org.argeo.security.dao.os/pom.xml
new file mode 100644 (file)
index 0000000..94fd879
--- /dev/null
@@ -0,0 +1,27 @@
+<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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.dao.os</artifactId>
+       <name>Commons Security DAO OS</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Import-Package>
+                                                       *,
+                                                       org.argeo.jcr,
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.dao.os/security.properties b/trunk/security/modules/org.argeo.security.dao.os/security.properties
new file mode 100644 (file)
index 0000000..ae77bf0
--- /dev/null
@@ -0,0 +1,2 @@
+argeo.security.systemKey=argeo
+argeo.node.repo.securityWorkspace=security
diff --git a/trunk/security/modules/org.argeo.security.webapp/.project b/trunk/security/modules/org.argeo.security.webapp/.project
new file mode 100644 (file)
index 0000000..f28005e
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.security.webapp</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/security/modules/org.argeo.security.webapp/WEB-INF/applicationContext.xml b/trunk/security/modules/org.argeo.security.webapp/WEB-INF/applicationContext.xml
new file mode 100644 (file)
index 0000000..899afa3
--- /dev/null
@@ -0,0 +1,19 @@
+<?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:aop="http://www.springframework.org/schema/aop"
+       xmlns:tx="http://www.springframework.org/schema/tx"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
+       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
+
+       <import resource="security.xml" />
+       <import resource="osgi.xml" />
+
+       <!--  Properties -->
+       <bean
+               class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer"
+               lazy-init="false">
+               <property name="contextOverride" value="true" />
+       </bean>
+</beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.webapp/WEB-INF/osgi.xml b/trunk/security/modules/org.argeo.security.webapp/WEB-INF/osgi.xml
new file mode 100644 (file)
index 0000000..39c74c8
--- /dev/null
@@ -0,0 +1,24 @@
+<?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
+       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
+\r
+       <reference id="_authenticationManager"\r
+               interface="org.springframework.security.AuthenticationManager" />\r
+       <reference id="userDetailsService"\r
+               interface="org.springframework.security.userdetails.UserDetailsService" />\r
+\r
+       <reference id="securityService" interface="org.argeo.security.ArgeoSecurityService" />\r
+\r
+       <list id="objectFactories" interface="org.argeo.server.json.JsonObjectFactory"\r
+               cardinality="0..N" />\r
+\r
+       <!--\r
+               <service ref="authenticationProcessingFilterEntryPoint"\r
+               interface="org.springframework.security.ui.AuthenticationEntryPoint"\r
+               />\r
+       -->\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.webapp/WEB-INF/security-servlet.xml b/trunk/security/modules/org.argeo.security.webapp/WEB-INF/security-servlet.xml
new file mode 100644 (file)
index 0000000..bab0a82
--- /dev/null
@@ -0,0 +1,41 @@
+<?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:aop="http://www.springframework.org/schema/aop"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 
+       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
+       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
+
+       <context:component-scan base-package="org.argeo.security.mvc" />
+
+       <bean id="controller" class="org.argeo.security.mvc.UsersRolesController">
+               <property name="securityService" ref="securityService" />
+               <property name="userDeserializer" ref="userDeserializer">
+               </property>
+       </bean>
+
+       <bean id="userDeserializer" class="org.argeo.server.json.JsonServerMapper">
+               <property name="targetClass" value="org.argeo.security.SimpleArgeoUser" />
+               <property name="deserializers">
+                       <map>
+                               <entry key="org.argeo.security.UserNature">
+                                       <bean class="org.argeo.server.json.GenericJsonDeserializer">
+                                               <property name="objectFactories" ref="objectFactories" />
+                                       </bean>
+                               </entry>
+                       </map>
+               </property>
+       </bean>
+
+
+       <bean id="viewResolver" class="org.argeo.server.mvc.SerializingViewResolver">
+               <property name="serializer" ref="serverMapper" />
+       </bean>
+
+       <bean id="serverMapper" class="org.argeo.server.json.JsonServerMapper">
+       </bean>
+
+       <bean class="org.argeo.server.mvc.DefaultHandlerExceptionResolver" />
+
+</beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.webapp/WEB-INF/security.xml b/trunk/security/modules/org.argeo.security.webapp/WEB-INF/security.xml
new file mode 100644 (file)
index 0000000..66e62cf
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:security="http://www.springframework.org/schema/security"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
+
+       <bean id="authenticationProcessingFilterEntryPoint"
+               class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint">
+               <property name="loginFormUrl" value="/getCredentials.ria" />
+               <property name="forceHttps" value="false" />
+       </bean>
+
+       <security:http entry-point-ref="authenticationProcessingFilterEntryPoint">
+               <security:intercept-url pattern="/*.security"
+                       access="ROLE_USER,ROLE_ADMIN" />
+               <security:intercept-url pattern="/*.ria"
+                       access="ROLE_ANONYMOUS,ROLE_USER" />
+               <security:logout logout-url="/logout.ria"
+                       logout-success-url="/getCredentials.ria" />
+               <security:anonymous username="anonymous"
+                       granted-authority="ROLE_ANONYMOUS" />
+               <security:remember-me key="argeo" services-ref="rememberMeServices" />
+       </security:http>
+
+       <bean id="rememberMeServices" class="org.argeo.security.mvc.ArgeoRememberMeServices">
+               <property name="alwaysRemember" value="true" />
+               <property name="userDetailsService" ref="userDetailsService" />
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+
+       <bean id="authenticationProcessingFilter"
+               class="org.springframework.security.ui.webapp.AuthenticationProcessingFilter">
+               <security:custom-filter position="AUTHENTICATION_PROCESSING_FILTER" />
+               <property name="authenticationManager" ref="_authenticationManager" />
+               <property name="authenticationFailureUrl" value="/getCredentials.ria" />
+               <property name="defaultTargetUrl" value="/getCredentials.ria" />
+               <property name="filterProcessesUrl" value="/login.ria" />
+               <property name="usernameParameter" value="username" />
+               <property name="passwordParameter" value="password" />
+       </bean>
+</beans>
\ No newline at end of file
diff --git a/trunk/security/modules/org.argeo.security.webapp/WEB-INF/web.xml b/trunk/security/modules/org.argeo.security.webapp/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..2331287
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+       version="2.5">
+
+       <display-name>Argeo Security Web Application</display-name>
+
+       <!-- SECURITY servlet -->
+       <servlet>
+               <servlet-name>security</servlet-name>
+               <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+               <init-param>
+                       <param-name>contextClass</param-name>
+                       <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
+               </init-param>
+               <load-on-startup>1</load-on-startup>
+       </servlet>
+       <servlet-mapping>
+               <servlet-name>security</servlet-name>
+               <url-pattern>*.security</url-pattern>
+       </servlet-mapping>
+       <servlet-mapping>
+               <servlet-name>security</servlet-name>
+               <url-pattern>*.ria</url-pattern>
+       </servlet-mapping>
+
+       <!-- General -->
+       <context-param>
+               <param-name>contextConfigLocation</param-name>
+               <param-value>/WEB-INF/applicationContext.xml</param-value>
+       </context-param>
+
+       <listener>
+               <display-name>Spring Context</display-name>
+               <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+       </listener>
+       <context-param>
+               <param-name>contextClass</param-name>
+               <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
+       </context-param>
+
+       <!--  Security -->
+       <filter>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
+       </filter>
+
+       <filter-mapping>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <url-pattern>/*</url-pattern>
+       </filter-mapping>
+
+       <context-param>
+               <param-name>argeo.security.systemKey</param-name>
+               <param-value>argeo</param-value>
+       </context-param>
+</web-app>
diff --git a/trunk/security/modules/org.argeo.security.webapp/pom.xml b/trunk/security/modules/org.argeo.security.webapp/pom.xml
new file mode 100644 (file)
index 0000000..e949174
--- /dev/null
@@ -0,0 +1,45 @@
+<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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.webapp</artifactId>
+       <name>Commons Security Webapp</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Web-ContextPath>org.argeo.security.webapp</Web-ContextPath>
+                                               <Import-Package>
+                                                       *,
+                                                       javax.servlet,
+                                                       javax.servlet.http,
+                                                       javax.servlet.resources,
+                                                       org.argeo.security,
+                                                       org.argeo.security.mvc,
+                                                       org.argeo.server.mvc,
+                                                       org.springframework.ldap.core.support,
+                                                       org.springframework.osgi.web.context.support,
+                                                       org.springframework.security,
+                                                       org.springframework.security.config,
+                                                       org.springframework.security.ui,
+                                                       org.springframework.security.ui.rememberme,
+                                                       org.springframework.security.ui.webapp,
+                                                       org.springframework.security.userdetails,
+                                                       org.springframework.web.context,
+                                                       org.springframework.web.context.support,
+                                                       org.springframework.web.filter,
+                                                       org.springframework.web.servlet
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/security/modules/pom.xml b/trunk/security/modules/pom.xml
new file mode 100644 (file)
index 0000000..21e5b3b
--- /dev/null
@@ -0,0 +1,37 @@
+<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.11</version>
+               <artifactId>security</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <groupId>org.argeo.commons.security</groupId>
+       <artifactId>modules</artifactId>
+       <packaging>pom</packaging>
+       <name>Commons Security Modules</name>
+       <modules>
+               <module>org.argeo.security.dao.cli</module>
+               <module>org.argeo.security.dao.os</module>
+               <module>org.argeo.security.dao.jackrabbit</module>
+               <module>org.argeo.security.dao.ldap</module>
+               <module>org.argeo.security.auth.ldap</module>
+               <module>org.argeo.security.webapp</module>
+       </modules>
+       <build>
+               <resources>
+                       <resource>
+                               <directory>.</directory>
+                               <includes>
+                                       <include>**</include>
+                               </includes>
+                               <excludes>
+                                       <exclude>.*</exclude>
+                                       <exclude>.*/**</exclude>
+                                       <exclude>pom.xml</exclude>
+                                       <exclude>build.properties</exclude>
+                               </excludes>
+                       </resource>
+               </resources>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.equinox/.classpath b/trunk/security/plugins/org.argeo.security.equinox/.classpath
new file mode 100644 (file)
index 0000000..8487250
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/security/plugins/org.argeo.security.equinox/.project b/trunk/security/plugins/org.argeo.security.equinox/.project
new file mode 100644 (file)
index 0000000..e4dd594
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.security.equinox</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/trunk/security/plugins/org.argeo.security.equinox/.settings/org.eclipse.pde.core.prefs b/trunk/security/plugins/org.argeo.security.equinox/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..87ab381
--- /dev/null
@@ -0,0 +1,4 @@
+#Sun Jan 16 11:19:07 CET 2011
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/trunk/security/plugins/org.argeo.security.equinox/META-INF/spring/loginModules.xml b/trunk/security/plugins/org.argeo.security.equinox/META-INF/spring/loginModules.xml
new file mode 100644 (file)
index 0000000..7a3e802
--- /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">
+
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:security.properties</value>
+               </property>
+       </bean>
+
+       <bean id="springLoginModule" class="org.argeo.security.equinox.SpringLoginModule"
+               scope="prototype">
+               <property name="authenticationManager" ref="authenticationManager" />
+               <property name="availableLocales" value="${argeo.i18n.availableLocales}"/>
+       </bean>
+
+       <bean id="springLoginModuleRemote" class="org.argeo.security.equinox.SpringLoginModule"
+               scope="prototype">
+               <property name="remote" value="true" />
+               <property name="authenticationManager" ref="authenticationManager" />
+       </bean>
+
+       <bean id="anonymousSpringLoginModule" class="org.argeo.security.equinox.SpringLoginModule"
+               scope="prototype">
+               <property name="anonymous" value="true" />
+               <property name="anonymousRole" value="${argeo.security.anonymousRole}" />
+               <property name="key" value="${argeo.security.systemKey}" />
+               <property name="authenticationManager" ref="authenticationManager" />
+       </bean>
+
+       <bean id="osSpringLoginModule" class="org.argeo.security.equinox.OsSpringLoginModule"
+               scope="prototype">
+               <property name="authenticationManager" ref="authenticationManager" />
+       </bean>
+</beans>
diff --git a/trunk/security/plugins/org.argeo.security.equinox/META-INF/spring/osgi.xml b/trunk/security/plugins/org.argeo.security.equinox/META-INF/spring/osgi.xml
new file mode 100644 (file)
index 0000000..8003fb2
--- /dev/null
@@ -0,0 +1,14 @@
+<?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="authenticationManager"\r
+               interface="org.springframework.security.AuthenticationManager"\r
+               context-class-loader="client" />\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.equinox/build.properties b/trunk/security/plugins/org.argeo.security.equinox/build.properties
new file mode 100644 (file)
index 0000000..3e2615f
--- /dev/null
@@ -0,0 +1,4 @@
+bin.includes = META-INF/,\
+               plugin.xml
+source.. = src/main/java/
+output.. = target/classes/
diff --git a/trunk/security/plugins/org.argeo.security.equinox/plugin.xml b/trunk/security/plugins/org.argeo.security.equinox/plugin.xml
new file mode 100644 (file)
index 0000000..2cc81f1
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+  <!-- Argeo -->
+  <extension id="springLoginModule" name="Argeo Spring" point="org.eclipse.equinox.security.loginModule">
+  <loginModule class="org.argeo.eclipse.spring.SpringExtensionFactory" description="Username/password authentication"/>
+  </extension>
+
+  <extension id="springLoginModuleRemote" name="Argeo Spring Remote" point="org.eclipse.equinox.security.loginModule">
+  <loginModule class="org.argeo.eclipse.spring.SpringExtensionFactory" description="Delegates authentication to a remote URL (typically JCR)"/>
+  </extension>
+
+  <extension id="anonymousSpringLoginModule" name="Argeo Spring Anonymous" point="org.eclipse.equinox.security.loginModule">
+  <loginModule class="org.argeo.eclipse.spring.SpringExtensionFactory" description="Public access without authentication"/>
+  </extension>
+
+  <extension id="osSpringLoginModule" name="Argeo Spring OS" point="org.eclipse.equinox.security.loginModule">
+  <loginModule class="org.argeo.eclipse.spring.SpringExtensionFactory" description="Use the operating system authentication of the JVM"/>
+  </extension>
+  
+  <!-- Java -->
+  <extension id="unixLoginModule" name="UNIX" point="org.eclipse.equinox.security.loginModule">
+  <loginModule class="com.sun.security.auth.module.UnixLoginModule" description="UNIX Login Module"/>
+  </extension>
+   
+  <extension id="keyStoreLoginModule" name="Keystore" point="org.eclipse.equinox.security.loginModule">
+  <loginModule class="com.sun.security.auth.module.KeyStoreLoginModule" description="Keystore Login Module"/>
+  </extension>
+   
+  <extension id="ntLoginModule" name="Windows" point="org.eclipse.equinox.security.loginModule">
+  <loginModule class="com.sun.security.auth.module.NTLoginModule" description="Windows Login Module"/>
+  </extension>
+
+  <!-- Spring -->
+  <extension id="springSecurityContextLoginModule" name="Spring" point="org.eclipse.equinox.security.loginModule">
+  <loginModule class="org.springframework.security.providers.jaas.SecurityContextLoginModule" description="Raw Spring Login Module"/>
+  </extension>
+</plugin>
diff --git a/trunk/security/plugins/org.argeo.security.equinox/pom.xml b/trunk/security/plugins/org.argeo.security.equinox/pom.xml
new file mode 100644 (file)
index 0000000..040a965
--- /dev/null
@@ -0,0 +1,74 @@
+<?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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>plugins</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.equinox</artifactId>
+       <name>Commons Security Equinox</name>
+       <packaging>jar</packaging>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Import-Package>*,
+                                                       org.springframework.core,
+                                                       org.argeo.eclipse.spring
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+
+               <!-- Eclipse -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.eclipse.osgi</artifactId>
+               </dependency>
+               
+               <!-- RCP only dependency, needed at compile time -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.dep.rcp</artifactId>
+                       <version>2.1.11</version>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- Commons -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.core</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.security.core</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.equinox/security.properties b/trunk/security/plugins/org.argeo.security.equinox/security.properties
new file mode 100644 (file)
index 0000000..70e75cc
--- /dev/null
@@ -0,0 +1,5 @@
+argeo.security.systemKey=argeo
+
+argeo.security.anonymousRole=ROLE_ANONYMOUS
+
+argeo.i18n.availableLocales=
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/OsSpringLoginModule.java b/trunk/security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/OsSpringLoginModule.java
new file mode 100644 (file)
index 0000000..1a7ebb4
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.equinox;
+
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.security.OsAuthenticationToken;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationManager;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.providers.jaas.SecurityContextLoginModule;
+
+/** Login module which caches one subject per thread. */
+public class OsSpringLoginModule extends SecurityContextLoginModule {
+       // private final static Log log =
+       // LogFactory.getLog(OsSpringLoginModule.class);
+
+       private AuthenticationManager authenticationManager;
+
+       private Subject subject;
+
+       public OsSpringLoginModule() {
+
+       }
+
+       @SuppressWarnings("rawtypes")
+       public void initialize(Subject subject, CallbackHandler callbackHandler,
+                       Map sharedState, Map options) {
+               super.initialize(subject, callbackHandler, sharedState, options);
+               this.subject = subject;
+       }
+
+       public boolean login() throws LoginException {
+               // thread already logged in
+               if (SecurityContextHolder.getContext().getAuthentication() != null)
+                       return super.login();
+
+               OsAuthenticationToken oat = new OsAuthenticationToken();
+               Authentication authentication = authenticationManager.authenticate(oat);
+               registerAuthentication(authentication);
+               return super.login();
+       }
+
+       @Override
+       public boolean logout() throws LoginException {
+               subject.getPrincipals().clear();
+               return super.logout();
+       }
+
+       /**
+        * Register an {@link Authentication} in the security context.
+        * 
+        * @param authentication
+        *            has to implement {@link Authentication}.
+        */
+       protected void registerAuthentication(Object authentication) {
+               SecurityContextHolder.getContext().setAuthentication(
+                               (Authentication) authentication);
+       }
+
+       public void setAuthenticationManager(
+                       AuthenticationManager authenticationManager) {
+               this.authenticationManager = authenticationManager;
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/SpringLoginModule.java b/trunk/security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/SpringLoginModule.java
new file mode 100644 (file)
index 0000000..6fd179e
--- /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.equinox;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.LoginException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.security.NodeAuthenticationToken;
+import org.argeo.util.LocaleCallback;
+import org.argeo.util.LocaleUtils;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationManager;
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
+import org.springframework.security.providers.jaas.SecurityContextLoginModule;
+
+/** Login module which caches one subject per thread. */
+public class SpringLoginModule extends SecurityContextLoginModule {
+       final static String NODE_REPO_URI = "argeo.node.repo.uri";
+
+       private final static Log log = LogFactory.getLog(SpringLoginModule.class);
+
+       private AuthenticationManager authenticationManager;
+
+       private CallbackHandler callbackHandler;
+
+       private Subject subject;
+
+       private Long waitBetweenFailedLoginAttempts = 5 * 1000l;
+
+       private Boolean remote = false;
+       private Boolean anonymous = false;
+       /** Comma separated list of locales */
+       private String availableLocales = "";
+
+       private String key = null;
+       private String anonymousRole = "ROLE_ANONYMOUS";
+
+       public SpringLoginModule() {
+
+       }
+
+       @SuppressWarnings("rawtypes")
+       public void initialize(Subject subject, CallbackHandler callbackHandler,
+                       Map sharedState, Map options) {
+               super.initialize(subject, callbackHandler, sharedState, options);
+               this.callbackHandler = callbackHandler;
+               this.subject = subject;
+       }
+
+       public boolean login() throws LoginException {
+               try {
+                       // thread already logged in
+                       if (SecurityContextHolder.getContext().getAuthentication() != null)
+                               return super.login();
+
+                       if (remote && anonymous)
+                               throw new LoginException(
+                                               "Cannot have a Spring login module which is remote and anonymous");
+
+                       // reset all principals and credentials
+                       if (log.isTraceEnabled())
+                               log.trace("Resetting all principals and credentials of "
+                                               + subject);
+                       if (subject.getPrincipals() != null)
+                               subject.getPrincipals().clear();
+                       if (subject.getPrivateCredentials() != null)
+                               subject.getPrivateCredentials().clear();
+                       if (subject.getPublicCredentials() != null)
+                               subject.getPublicCredentials().clear();
+
+                       Locale selectedLocale = null;
+                       // deals first with public access since it's simple
+                       if (anonymous) {
+                               // multi locale
+                               if (callbackHandler != null && availableLocales != null
+                                               && !availableLocales.trim().equals("")) {
+                                       LocaleCallback localeCallback = new LocaleCallback(
+                                                       availableLocales);
+                                       callbackHandler.handle(new Callback[] { localeCallback });
+                                       selectedLocale = localeCallback.getSelectedLocale();
+                               }
+
+                               // TODO integrate with JCR?
+                               Object principal = UUID.randomUUID().toString();
+                               GrantedAuthority[] authorities = { new GrantedAuthorityImpl(
+                                               anonymousRole) };
+                               AnonymousAuthenticationToken anonymousToken = new AnonymousAuthenticationToken(
+                                               key, principal, authorities);
+                               Authentication auth = authenticationManager
+                                               .authenticate(anonymousToken);
+                               registerAuthentication(auth);
+                       } else {
+                               if (callbackHandler == null)
+                                       throw new LoginException("No call back handler available");
+
+                               // ask for username and password
+                               NameCallback nameCallback = new NameCallback("User");
+                               PasswordCallback passwordCallback = new PasswordCallback(
+                                               "Password", false);
+                               final String defaultNodeUrl = System
+                                               .getProperty(NODE_REPO_URI,
+                                                               "http://localhost:7070/org.argeo.jcr.webapp/remoting/node");
+                               NameCallback urlCallback = new NameCallback("Site URL",
+                                               defaultNodeUrl);
+                               LocaleCallback localeCallback = new LocaleCallback(
+                                               availableLocales);
+
+                               // handle callbacks
+                               if (remote)
+                                       callbackHandler.handle(new Callback[] { nameCallback,
+                                                       passwordCallback, urlCallback, localeCallback });
+                               else
+                                       callbackHandler.handle(new Callback[] { nameCallback,
+                                                       passwordCallback, localeCallback });
+
+                               selectedLocale = localeCallback.getSelectedLocale();
+
+                               // create credentials
+                               String username = nameCallback.getName();
+                               if (username == null || username.trim().equals(""))
+                                       return false;
+
+                               String password = "";
+                               if (passwordCallback.getPassword() != null)
+                                       password = String.valueOf(passwordCallback.getPassword());
+
+                               NodeAuthenticationToken credentials;
+                               if (remote) {
+                                       String url = urlCallback.getName();
+                                       credentials = new NodeAuthenticationToken(username,
+                                                       password, url);
+                               } else {
+                                       credentials = new NodeAuthenticationToken(username,
+                                                       password);
+                               }
+
+                               Authentication authentication;
+                               try {
+                                       authentication = authenticationManager
+                                                       .authenticate(credentials);
+                               } catch (BadCredentialsException e) {
+                                       // wait between failed login attempts
+                                       Thread.sleep(waitBetweenFailedLoginAttempts);
+                                       throw e;
+                               }
+                               registerAuthentication(authentication);
+                       }
+
+                       if (selectedLocale != null)
+                               LocaleUtils.threadLocale.set(selectedLocale);
+
+                       return super.login();
+               } catch (LoginException e) {
+                       throw e;
+               } catch (ThreadDeath e) {
+                       LoginException le = new LoginException(
+                                       "Spring Security login thread died");
+                       le.initCause(e);
+                       throw le;
+               } catch (Exception e) {
+                       LoginException le = new LoginException(
+                                       "Spring Security login failed");
+                       le.initCause(e);
+                       throw le;
+               }
+       }
+
+       @Override
+       public boolean logout() throws LoginException {
+               subject.getPrincipals().clear();
+               return super.logout();
+       }
+
+       /**
+        * Register an {@link Authentication} in the security context.
+        * 
+        * @param authentication
+        *            has to implement {@link Authentication}.
+        */
+       protected void registerAuthentication(Object authentication) {
+               SecurityContextHolder.getContext().setAuthentication(
+                               (Authentication) authentication);
+       }
+
+       public void setAuthenticationManager(
+                       AuthenticationManager authenticationManager) {
+               this.authenticationManager = authenticationManager;
+       }
+
+       /** Authenticates on a remote node */
+       public void setRemote(Boolean remote) {
+               this.remote = remote;
+       }
+
+       /**
+        * Request anonymous authentication (incompatible with remote)
+        */
+       public void setAnonymous(Boolean anonymous) {
+               this.anonymous = anonymous;
+       }
+
+       /** Role identifying an anonymous user */
+       public void setAnonymousRole(String anonymousRole) {
+               this.anonymousRole = anonymousRole;
+       }
+
+       /** System key */
+       public void setKey(String key) {
+               this.key = key;
+       }
+
+       public void setAvailableLocales(String locales) {
+               this.availableLocales = locales;
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/.classpath b/trunk/security/plugins/org.argeo.security.ui.admin/.classpath
new file mode 100644 (file)
index 0000000..8487250
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/.project b/trunk/security/plugins/org.argeo.security.ui.admin/.project
new file mode 100644 (file)
index 0000000..9821dd9
--- /dev/null
@@ -0,0 +1,28 @@
+<?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/trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/commands.xml b/trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/commands.xml
new file mode 100644 (file)
index 0000000..e0a435e
--- /dev/null
@@ -0,0 +1,52 @@
+<?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="openArgeoUserEditor"
+               class="org.argeo.security.ui.admin.commands.OpenArgeoUserEditor"
+               scope="prototype" />
+
+       <!-- USERS -->
+       <bean id="newUser" class="org.argeo.security.ui.admin.commands.NewUser"
+               scope="prototype">
+               <property name="repository" ref="nodeRepository" />
+               <property name="userAdminService" ref="userAdminService" />
+               <property name="jcrSecurityModel" ref="jcrSecurityModel" />
+       </bean>
+
+       <bean id="refreshUsersList" class="org.argeo.security.ui.admin.commands.RefreshUsersList"
+               scope="prototype">
+               <property name="userAdminService" ref="userAdminService" />
+               <property name="repository" ref="nodeRepository" />
+       </bean>
+
+       <bean id="deleteUser" class="org.argeo.security.ui.admin.commands.DeleteUser"
+               scope="prototype">
+               <property name="userAdminService" ref="userAdminService" />
+       </bean>
+       
+       <bean id="userBatchUpdate" class="org.argeo.security.ui.admin.commands.UserBatchUpdate"
+               scope="prototype">
+               <property name="repository" ref="nodeRepository" />
+               <property name="userAdminService" ref="userAdminService" />
+               <property name="jcrSecurityModel" ref="jcrSecurityModel" />
+       </bean>
+       
+
+       <!-- ROLES -->
+       <bean id="refreshRoles" class="org.argeo.security.ui.admin.commands.RefreshRoles"
+               scope="prototype">
+       </bean>
+
+       <bean id="addRole" class="org.argeo.security.ui.admin.commands.AddRole"
+               scope="prototype">
+               <property name="userAdminService" ref="userAdminService" />
+       </bean>
+
+       <bean id="deleteRole" class="org.argeo.security.ui.admin.commands.DeleteRole"
+               scope="prototype">
+               <property name="userAdminService" ref="userAdminService" />
+       </bean>
+</beans>
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/common.xml b/trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/common.xml
new file mode 100644 (file)
index 0000000..737f304
--- /dev/null
@@ -0,0 +1,20 @@
+<?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\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
+       <!-- FIXME Hard-coded dependency to Jackrabbit for user management -->\r
+       <bean name="jcrSecurityModel" class="org.argeo.security.jackrabbit.JackrabbitSecurityModel" />\r
+\r
+       <!-- Old deprecated way. -->\r
+       <!-- <bean id="session" class="org.argeo.security.jcr.SecureThreadBoundSession"> \r
+               <property name="repository" ref="nodeRepository" /> </bean> -->\r
+</beans>
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/editors.xml b/trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/editors.xml
new file mode 100644 (file)
index 0000000..740792a
--- /dev/null
@@ -0,0 +1,13 @@
+<?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="adminArgeoUserEditor" class="org.argeo.security.ui.admin.editors.ArgeoUserEditor"
+               scope="prototype">
+               <property name="userAdminService" ref="userAdminService" />
+               <property name="repository" ref="nodeRepository" />
+       </bean>
+</beans>
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/osgi.xml b/trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/osgi.xml
new file mode 100644 (file)
index 0000000..2fa5144
--- /dev/null
@@ -0,0 +1,14 @@
+<?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
+       <reference id="userAdminService" interface="org.argeo.security.UserAdminService" />\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/views.xml b/trunk/security/plugins/org.argeo.security.ui.admin/META-INF/spring/views.xml
new file mode 100644 (file)
index 0000000..2ddb05f
--- /dev/null
@@ -0,0 +1,17 @@
+<?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="adminUsersView" class="org.argeo.security.ui.admin.views.UsersView"
+               scope="prototype">
+               <property name="repository" ref="nodeRepository" />
+               <!-- <property name="session" ref="session" />  -->
+       </bean>
+       
+       <bean id="adminRolesView" class="org.argeo.security.ui.admin.views.RolesView"
+               scope="prototype">
+               <property name="userAdminService" ref="userAdminService" />
+       </bean>
+</beans>
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/build.properties b/trunk/security/plugins/org.argeo.security.ui.admin/build.properties
new file mode 100644 (file)
index 0000000..c41623e
--- /dev/null
@@ -0,0 +1,5 @@
+bin.includes = plugin.xml,\
+               META-INF/,\
+               security-admin.properties
+source.. = src/main/java/
+output.. = target/classes/
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/icons/add.gif b/trunk/security/plugins/org.argeo.security.ui.admin/icons/add.gif
new file mode 100644 (file)
index 0000000..252d7eb
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.admin/icons/add.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/icons/batch.gif b/trunk/security/plugins/org.argeo.security.ui.admin/icons/batch.gif
new file mode 100644 (file)
index 0000000..b8ca14a
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.admin/icons/batch.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/icons/clear.gif b/trunk/security/plugins/org.argeo.security.ui.admin/icons/clear.gif
new file mode 100644 (file)
index 0000000..6bc10f9
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.admin/icons/clear.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/icons/refresh.png b/trunk/security/plugins/org.argeo.security.ui.admin/icons/refresh.png
new file mode 100644 (file)
index 0000000..a3884fb
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.admin/icons/refresh.png differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/icons/remove.gif b/trunk/security/plugins/org.argeo.security.ui.admin/icons/remove.gif
new file mode 100644 (file)
index 0000000..0ae6dec
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.admin/icons/remove.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/icons/role.gif b/trunk/security/plugins/org.argeo.security.ui.admin/icons/role.gif
new file mode 100644 (file)
index 0000000..274a850
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.admin/icons/role.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/icons/save.gif b/trunk/security/plugins/org.argeo.security.ui.admin/icons/save.gif
new file mode 100644 (file)
index 0000000..654ad7b
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.admin/icons/save.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/icons/save_security.png b/trunk/security/plugins/org.argeo.security.ui.admin/icons/save_security.png
new file mode 100644 (file)
index 0000000..ca41dc9
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.admin/icons/save_security.png differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/icons/save_security_disabled.png b/trunk/security/plugins/org.argeo.security.ui.admin/icons/save_security_disabled.png
new file mode 100644 (file)
index 0000000..fb7d08d
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.admin/icons/save_security_disabled.png differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/icons/security.gif b/trunk/security/plugins/org.argeo.security.ui.admin/icons/security.gif
new file mode 100644 (file)
index 0000000..57fb95e
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.admin/icons/security.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/icons/sync.gif b/trunk/security/plugins/org.argeo.security.ui.admin/icons/sync.gif
new file mode 100644 (file)
index 0000000..b4fa052
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.admin/icons/sync.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/icons/user.gif b/trunk/security/plugins/org.argeo.security.ui.admin/icons/user.gif
new file mode 100644 (file)
index 0000000..90a0014
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.admin/icons/user.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/icons/users.gif b/trunk/security/plugins/org.argeo.security.ui.admin/icons/users.gif
new file mode 100644 (file)
index 0000000..2de7edd
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.admin/icons/users.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/plugin.xml b/trunk/security/plugins/org.argeo.security.ui.admin/plugin.xml
new file mode 100644 (file)
index 0000000..e260bde
--- /dev/null
@@ -0,0 +1,153 @@
+<?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>
+   <extension
+         point="org.eclipse.ui.views">
+      <view
+            class="org.argeo.eclipse.spring.SpringExtensionFactory"
+            icon="icons/users.gif"
+            id="org.argeo.security.ui.admin.adminUsersView"
+            name="Users"
+            restorable="false">
+      </view>
+      <view
+            class="org.argeo.eclipse.spring.SpringExtensionFactory"
+            icon="icons/role.gif"
+            id="org.argeo.security.ui.admin.adminRolesView"
+            name="Roles"
+            restorable="false">
+      </view>
+   </extension>
+   <extension
+           point="org.eclipse.ui.editors">
+               <editor
+                 class="org.argeo.eclipse.spring.SpringExtensionFactory"
+              id="org.argeo.security.ui.admin.adminArgeoUserEditor"
+              name="User"
+              icon="icons/user.gif"
+              default="false">
+        </editor>
+     </extension>
+    <extension
+         point="org.eclipse.ui.commands">
+      <command
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            id="org.argeo.security.ui.admin.openArgeoUserEditor"
+            name="OpenArgeoUserEditor">
+                       <commandParameter
+                       id="org.argeo.security.ui.admin.username"
+                       name="Username">
+                       </commandParameter>
+      </command>
+      <command
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            id="org.argeo.security.ui.admin.refreshUsersList"
+            name="refreshUsersList">
+      </command>
+      <command
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            id="org.argeo.security.ui.admin.newUser"
+            name="New User">
+      </command>
+      <command
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            id="org.argeo.security.ui.admin.deleteUser"
+            name="deleteUser">
+      </command>
+      <command
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            id="org.argeo.security.ui.admin.refreshRoles"
+            name="refreshRoles">
+      </command>
+      <command
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            id="org.argeo.security.ui.admin.addRole"
+            name="AddRole">
+      </command>
+      <command
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            id="org.argeo.security.ui.admin.deleteRole"
+            name="deleteRole">
+      </command>
+      <command
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            id="org.argeo.security.ui.admin.userBatchUpdate"
+            name="User batch update">
+      </command>
+    </extension>
+     <extension
+         point="org.eclipse.ui.menus">
+       <menuContribution
+            locationURI="toolbar:org.argeo.security.ui.admin.adminRolesView">
+            <command
+                  commandId="org.argeo.security.ui.admin.deleteRole"
+                  icon="icons/remove.gif"
+                  label="Delete Role"
+                  tooltip="Delete selected roles">
+            </command>
+            <command
+                  commandId="org.argeo.security.ui.admin.addRole"
+                  icon="icons/add.gif"
+                  label="Add Role"
+                  tooltip="Add new role">
+            </command>
+            <command
+                  commandId="org.argeo.security.ui.admin.refreshRoles"
+                  icon="icons/sync.gif"
+                  label="LDAP Roles Sync"
+                  tooltip="Synchronize roles from LDAP">
+            </command>
+        </menuContribution>
+       <menuContribution
+            locationURI="toolbar:org.argeo.security.ui.admin.adminUsersView">
+            <command
+                  commandId="org.argeo.security.ui.admin.refreshUsersList"
+                  icon="icons/refresh.png"
+                  label="Refresh list"
+                  tooltip="Force the full refresh of the user list">
+            </command>
+            <command
+                  commandId="org.argeo.security.ui.admin.deleteUser"
+                  icon="icons/remove.gif"
+                  label="Delete User"
+                  tooltip="Delete selected users">
+            </command>
+            <command
+                  commandId="org.argeo.security.ui.admin.newUser"
+                  icon="icons/add.gif"
+                  label="Add User"
+                  tooltip="Add 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>
+            <command
+                  commandId="org.argeo.security.ui.admin.refreshUsersList"
+                  icon="icons/sync.gif"
+                  label="LDAP Users Sync"
+                  tooltip="Synchronize users from LDAP">
+            </command>
+        </menuContribution>
+  </extension>
+  <extension
+           point="org.eclipse.ui.activities">
+        <!-- TODO: find a way to exclude evrything -->
+        <activityPatternBinding
+              activityId="org.argeo.security.ui.adminActivity"
+              isEqualityPattern="true"
+              pattern="org.argeo.security.ui.admin/org.argeo.security.ui.admin.adminSecurityPerspective">
+        </activityPatternBinding>
+     </extension>
+ </plugin>
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/pom.xml b/trunk/security/plugins/org.argeo.security.ui.admin/pom.xml
new file mode 100644 (file)
index 0000000..afdea9e
--- /dev/null
@@ -0,0 +1,112 @@
+<?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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>plugins</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.ui.admin</artifactId>
+       <name>Commons Security Admin UI</name>
+       <packaging>jar</packaging>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-ActivationPolicy>lazy</Bundle-ActivationPolicy>
+                                               <Bundle-Activator>org.argeo.security.ui.admin.SecurityAdminPlugin
+                                               </Bundle-Activator>
+                                               <Require-Bundle>org.eclipse.core.runtime</Require-Bundle>
+                                               <Import-Package>
+                                                       *,
+                                                       org.eclipse.swt,
+                                                       org.eclipse.ui.services,
+                                                       org.eclipse.jface.window,
+                                                       org.argeo.eclipse.spring,
+                                                       org.springframework.dao,
+                                                       org.springframework.core,
+                                               </Import-Package>
+                                               <Export-Package>org.argeo.security.ui.admin.*</Export-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- Argeo Security -->
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.ui</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.core</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Argeo Eclipse -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui.jcr</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- RCP only dependency, needed at compile time -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.dep.rcp</artifactId>
+                       <version>2.1.11</version>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui.rcp</artifactId>
+                       <version>2.1.11</version>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- Commons -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.security.core</artifactId>
+               </dependency>
+
+               <!-- Others -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging
+                       </artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/security-admin.properties b/trunk/security/plugins/org.argeo.security.ui.admin/security-admin.properties
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminImages.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminImages.java
new file mode 100644 (file)
index 0000000..f7ffa9c
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/** Shared icons that must be declared programmatically . */
+public class SecurityAdminImages {
+       @SuppressWarnings("unused")
+       private final static String PREFIX = "icons/";
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminPerspective.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminPerspective.java
new file mode 100644 (file)
index 0000000..c743f56
--- /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.views.RolesView;
+import org.argeo.security.ui.admin.views.UsersView;
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+
+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.65f, editorArea);
+               left.addView(UsersView.ID);
+               left.addView(RolesView.ID);
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminPlugin.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/SecurityAdminPlugin.java
new file mode 100644 (file)
index 0000000..06e9bd5
--- /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;
+
+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;
+
+       public SecurityAdminPlugin() {
+       }
+
+       public void start(BundleContext context) throws Exception {
+               super.start(context);
+               plugin = this;
+       }
+
+       public void stop(BundleContext context) throws Exception {
+               plugin = null;
+               super.stop(context);
+       }
+
+       public static SecurityAdminPlugin getDefault() {
+               return plugin;
+       }
+
+       public static ImageDescriptor getImageDescriptor(String path) {
+               return imageDescriptorFromPlugin(PLUGIN_ID, path);
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/UserTableComposite.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/UserTableComposite.java
new file mode 100644 (file)
index 0000000..720fade
--- /dev/null
@@ -0,0 +1,367 @@
+package org.argeo.security.ui.admin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.qom.Constraint;
+import javax.jcr.query.qom.Ordering;
+import javax.jcr.query.qom.QueryObjectModel;
+import javax.jcr.query.qom.QueryObjectModelFactory;
+import javax.jcr.query.qom.Selector;
+import javax.jcr.query.qom.StaticOperand;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.jcr.JcrUiUtils;
+import org.argeo.eclipse.ui.jcr.lists.ColumnDefinition;
+import org.argeo.eclipse.ui.jcr.lists.NodeViewerComparator;
+import org.argeo.eclipse.ui.jcr.lists.SimpleJcrNodeLabelProvider;
+import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils;
+import org.argeo.eclipse.ui.utils.ViewerUtils;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+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.graphics.Font;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+
+public class UserTableComposite extends Composite implements ArgeoNames {
+       // private final static Log log =
+       // LogFactory.getLog(UserTableComposite.class);
+
+       private static final long serialVersionUID = -7385959046279360420L;
+
+       private TableViewer usersViewer;
+       private Text filterTxt;
+       private final static String FILTER_HELP_MSG = "Type filter criterion "
+                       + "separated by a space";
+       private Session session;
+
+       private Font italic;
+       private Font bold;
+
+       private boolean hasFilter;
+       private boolean hasSelectionColumn;
+
+       // private List<Node> selectedItems = new ArrayList<Node>();
+
+       /**
+        * Overwrite to display other columns
+        */
+       public List<ColumnDefinition> getColumnsDef() {
+               List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+
+               // User ID
+               columnDefs.add(new ColumnDefinition(null, ARGEO_USER_ID,
+                               PropertyType.STRING, "User ID", 100));
+               // Displayed name
+               columnDefs.add(new ColumnDefinition(null, Property.JCR_TITLE,
+                               PropertyType.STRING, "Name", 150));
+
+               // E-mail
+               columnDefs.add(new ColumnDefinition(null, ARGEO_PRIMARY_EMAIL,
+                               PropertyType.STRING, "E-mail", 150));
+
+               // Description
+               columnDefs.add(new ColumnDefinition(null, Property.JCR_DESCRIPTION,
+                               PropertyType.STRING, "Description", 200));
+
+               return columnDefs;
+       }
+
+       public UserTableComposite(Composite parent, int style, Session session) {
+               super(parent, style);
+               this.session = session;
+       }
+
+       /**
+        * 
+        * @param addFilter
+        *            choose to add a field to filter results or not
+        * @param addSelection
+        *            choose to add a column to select some of the displayed results
+        *            or not
+        */
+       public void populate(boolean addFilter, boolean addSelection) {
+               // initialization
+               Composite parent = this;
+               italic = EclipseUiUtils.getItalicFont(parent);
+               bold = EclipseUiUtils.getBoldFont(parent);
+               hasFilter = addFilter;
+               hasSelectionColumn = addSelection;
+
+               // Main Layout
+               this.setLayout(new GridLayout(1, false));
+               if (hasFilter)
+                       createFilterPart(parent);
+               usersViewer = createTableViewer(parent);
+               EclipseUiSpecificUtils.enableToolTipSupport(usersViewer);
+               usersViewer.setContentProvider(new UsersContentProvider());
+               refreshFilteredList();
+       }
+
+       public List<Node> getSelectedUsers() {
+               if (hasSelectionColumn) {
+                       Object[] elements = ((CheckboxTableViewer) usersViewer)
+                                       .getCheckedElements();
+
+                       List<Node> result = new ArrayList<Node>();
+                       for (Object obj : elements) {
+                               result.add((Node) obj);
+                       }
+                       return result;
+               } else
+                       throw new ArgeoException("Unvalid request: no selection column "
+                                       + "has been created for the current table");
+       }
+
+       /** Returns the User table viewer, typically to add doubleclick listener */
+       public TableViewer getTableViewer() {
+               return usersViewer;
+       }
+       
+       /** Returns filter String or null*/
+       protected String getFilterString() {
+               return hasFilter ? filterTxt.getText() : null;
+       }
+       
+       
+
+       private TableViewer createTableViewer(final Composite parent) {
+               int style = SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL;
+               if (hasSelectionColumn)
+                       style = style | SWT.CHECK;
+
+               Table table = new Table(parent, style);
+               table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               TableViewer viewer;
+               if (hasSelectionColumn)
+                       viewer = new CheckboxTableViewer(table);
+               else
+                       viewer = new TableViewer(table);
+               table.setLinesVisible(true);
+               table.setHeaderVisible(true);
+
+               // pass a mapping between col index and property name to the comparator.
+               // List<String> propertiesList = new ArrayList<String>();
+
+               TableViewerColumn column;
+               int offset = 0;
+               if (hasSelectionColumn) {
+                       offset = 1;
+                       column = ViewerUtils.createTableViewerColumn(viewer, "", SWT.NONE,
+                                       25);
+                       column.setLabelProvider(new ColumnLabelProvider() {
+                               private static final long serialVersionUID = 1L;
+
+                               @Override
+                               public String getText(Object element) {
+                                       return null;
+                               }
+                       });
+                       SelectionAdapter selectionAdapter = new SelectionAdapter() {
+                               private static final long serialVersionUID = 1L;
+
+                               boolean allSelected = false;
+
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       allSelected = !allSelected;
+                                       ((CheckboxTableViewer) usersViewer)
+                                                       .setAllChecked(allSelected);
+                               }
+                       };
+                       column.getColumn().addSelectionListener(selectionAdapter);
+               }
+
+               // Create other columns
+               List<ColumnDefinition> colDefs = getColumnsDef();
+
+               NodeViewerComparator comparator = new NodeViewerComparator();
+               int i = offset;
+               for (ColumnDefinition colDef : colDefs) {
+                       column = ViewerUtils.createTableViewerColumn(viewer,
+                                       colDef.getHeaderLabel(), SWT.NONE, colDef.getColumnSize());
+                       column.setLabelProvider(new CLProvider(colDef.getPropertyName()));
+                       column.getColumn().addSelectionListener(
+                                       JcrUiUtils.getNodeSelectionAdapter(i,
+                                                       colDef.getPropertyType(), colDef.getPropertyName(),
+                                                       comparator, viewer));
+                       i++;
+               }
+
+               // IMPORTANT: initialize comparator before setting it
+               ColumnDefinition firstCol = colDefs.get(0);
+               comparator.setColumn(firstCol.getPropertyType(),
+                               firstCol.getPropertyName());
+               viewer.setComparator(comparator);
+
+               return viewer;
+       }
+
+       private class CLProvider extends SimpleJcrNodeLabelProvider {
+
+               private static final long serialVersionUID = 1L;
+
+               public CLProvider(String propertyName) {
+                       super(propertyName);
+               }
+
+               public String getToolTipText(Object element) {
+                       return getText(element);
+               }
+
+               @Override
+               public Font getFont(Object elem) {
+                       // self
+                       String username = getProperty(elem, ARGEO_USER_ID);
+                       if (username.equals(session.getUserID()))
+                               return bold;
+
+                       // disabled
+                       try {
+                               Node userProfile = (Node) elem;
+                               // Node userProfile = userHome.getNode(ARGEO_PROFILE);
+                               if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean())
+                                       return italic;
+                               else
+                                       return null;
+                       } catch (RepositoryException e) {
+                               throw new ArgeoException("Cannot get font for " + username, e);
+                       }
+               }
+       }
+
+       @Override
+       public boolean setFocus() {
+               usersViewer.getTable().setFocus();
+               return true;
+       }
+
+       @Override
+       public void dispose() {
+               super.dispose();
+       }
+
+       public void refresh() {
+               refreshFilteredList();
+       }
+
+       private String getProperty(Object element, String name) {
+               try {
+                       Node userProfile = (Node) element;
+                       return userProfile.hasProperty(name) ? userProfile
+                                       .getProperty(name).getString() : "";
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot get property " + name, e);
+               }
+       }
+
+       private class UsersContentProvider implements IStructuredContentProvider {
+               private static final long serialVersionUID = 1L;
+
+               public Object[] getElements(Object inputElement) {
+                       return (Object[]) inputElement;
+               }
+
+               public void dispose() {
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+       }
+
+       /* MANAGE FILTER */
+       private void createFilterPart(Composite parent) {
+               // Text Area for the filter
+               filterTxt = new Text(parent, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH
+                               | SWT.ICON_CANCEL);
+               filterTxt.setMessage(FILTER_HELP_MSG);
+               filterTxt.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
+                               | GridData.HORIZONTAL_ALIGN_FILL));
+               filterTxt.addModifyListener(new ModifyListener() {
+                       private static final long serialVersionUID = 1L;
+
+                       public void modifyText(ModifyEvent event) {
+                               refreshFilteredList();
+                       }
+               });
+       }
+
+       /**
+        * Refresh the user list: caller might overwrite in order to display a
+        * subset of all users, typically to remove current user from the list
+        */
+       protected void refreshFilteredList() {
+               List<Node> nodes;
+               try {
+                       nodes = JcrUtils.nodeIteratorToList(listFilteredElements(session,
+                                       hasFilter ? filterTxt.getText() : null));
+                       usersViewer.setInput(nodes.toArray());
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Unable to list users", e);
+               }
+       }
+
+       /**
+        * Build repository request : caller might overwrite in order to display a
+        * subset of all users
+        */
+       protected NodeIterator listFilteredElements(Session session, String filter)
+                       throws RepositoryException {
+               QueryManager queryManager = session.getWorkspace().getQueryManager();
+               QueryObjectModelFactory factory = queryManager.getQOMFactory();
+
+               Selector source = factory.selector(ArgeoTypes.ARGEO_USER_PROFILE,
+                               ArgeoTypes.ARGEO_USER_PROFILE);
+
+               // Dynamically build constraint depending on the filter String
+               Constraint defaultC = null;
+               if (filter != null && !"".equals(filter.trim())) {
+                       String[] strs = filter.trim().split(" ");
+                       for (String token : strs) {
+                               StaticOperand so = factory.literal(session.getValueFactory()
+                                               .createValue("*" + token + "*"));
+                               Constraint currC = factory.fullTextSearch(
+                                               source.getSelectorName(), null, so);
+                               if (defaultC == null)
+                                       defaultC = currC;
+                               else
+                                       defaultC = factory.and(defaultC, currC);
+                       }
+               }
+
+               Ordering order = factory.ascending(factory.propertyValue(
+                               source.getSelectorName(), ARGEO_USER_ID));
+               Ordering[] orderings = { order };
+
+               QueryObjectModel query = factory.createQuery(source, defaultC,
+                               orderings, null);
+
+               QueryResult result = query.execute();
+               return result.getNodes();
+       }
+}
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/AddRole.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/AddRole.java
new file mode 100644 (file)
index 0000000..a1008f6
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.commands;
+
+import org.argeo.ArgeoException;
+import org.argeo.security.UserAdminService;
+import org.argeo.security.ui.admin.editors.ArgeoUserEditor;
+import org.argeo.security.ui.admin.views.RolesView;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/** Add a new role. */
+public class AddRole extends AbstractHandler {
+       public final static String COMMAND_ID = "org.argeo.security.ui.admin.addRole";
+       private UserAdminService userAdminService;
+       private String rolePrefix = "ROLE_";
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               RolesView rolesView = (RolesView) HandlerUtil
+                               .getActiveWorkbenchWindow(event).getActivePage()
+                               .findView(RolesView.ID);
+               String role = rolesView.getNewRole();
+               if (role.trim().equals(""))
+                       return null;
+               if (role.equals(rolesView.getAddNewRoleText()))
+                       return null;
+               role = role.trim().toUpperCase();
+               if (!role.startsWith(rolePrefix))
+                       role = rolePrefix + role;
+               if (userAdminService.listEditableRoles().contains(role))
+                       throw new ArgeoException("Role " + role + " already exists");
+               userAdminService.newRole(role);
+               rolesView.refresh();
+
+               // refresh editors
+               IEditorReference[] refs = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage()
+                               .findEditors(null, ArgeoUserEditor.ID, IWorkbenchPage.MATCH_ID);
+               for (IEditorReference ref : refs) {
+                       ArgeoUserEditor userEditor = (ArgeoUserEditor) ref.getEditor(false);
+                       if (userEditor != null) {
+                               userEditor.refresh();
+                       }
+               }
+               return null;
+       }
+
+       public void setUserAdminService(UserAdminService userAdminService) {
+               this.userAdminService = userAdminService;
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/DeleteRole.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/DeleteRole.java
new file mode 100644 (file)
index 0000000..6cba11e
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.commands;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.argeo.security.UserAdminService;
+import org.argeo.security.ui.admin.views.RolesView;
+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.handlers.HandlerUtil;
+
+/** Deletes the selected roles */
+public class DeleteRole extends AbstractHandler {
+       private UserAdminService userAdminService;
+
+       @SuppressWarnings("unchecked")
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               ISelection selection = HandlerUtil.getCurrentSelection(event);
+               if (selection.isEmpty())
+                       return null;
+
+               List<String> toDelete = new ArrayList<String>();
+               Iterator<String> it = ((IStructuredSelection) selection).iterator();
+               while (it.hasNext()) {
+                       toDelete.add(it.next());
+               }
+
+               if (!MessageDialog
+                               .openQuestion(
+                                               HandlerUtil.getActiveShell(event),
+                                               "Delete Role",
+                                               "Are you sure that you want to delete "
+                                                               + toDelete
+                                                               + "?\n"
+                                                               + "This may lead to inconsistencies in the application."))
+                       return null;
+
+               for (String role : toDelete) {
+                       userAdminService.deleteRole(role);
+               }
+
+               RolesView view = (RolesView) HandlerUtil
+                               .getActiveWorkbenchWindow(event).getActivePage()
+                               .findView(RolesView.ID);
+               view.refresh();
+               return null;
+       }
+
+       public void setUserAdminService(UserAdminService userAdminService) {
+               this.userAdminService = userAdminService;
+       }
+}
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/DeleteUser.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/DeleteUser.java
new file mode 100644 (file)
index 0000000..ccf360f
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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.commands;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.security.UserAdminService;
+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.handlers.HandlerUtil;
+
+/** Deletes the selected user nodes */
+public class DeleteUser extends AbstractHandler {
+       private final static Log log = LogFactory.getLog(DeleteUser.class);
+
+       private UserAdminService userAdminService;
+
+       @SuppressWarnings("unchecked")
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               ISelection selection = HandlerUtil.getCurrentSelection(event);
+               if (selection.isEmpty())
+                       return null;
+
+               Map<String, Node> toDelete = new TreeMap<String, Node>();
+               Iterator<Node> it = ((IStructuredSelection) selection).iterator();
+               nodes: while (it.hasNext()) {
+                       Node profileNode = it.next();
+                       try {
+                               String userName = profileNode.getProperty(
+                                               ArgeoNames.ARGEO_USER_ID).getString();
+                               if (userName.equals(profileNode.getSession().getUserID())) {
+                                       log.warn("Cannot delete its own user: " + userName);
+                                       continue nodes;
+                               }
+                               toDelete.put(userName, profileNode);
+                       } catch (RepositoryException e) {
+                               log.warn("Cannot interpred user " + profileNode);
+                       }
+               }
+
+               if (!MessageDialog
+                               .openQuestion(
+                                               HandlerUtil.getActiveShell(event),
+                                               "Delete User",
+                                               "Are you sure that you want to delete users "
+                                                               + toDelete.keySet()
+                                                               + "?\n"
+                                                               + "This may lead to inconsistencies in the application."))
+                       return null;
+
+               for (String username : toDelete.keySet()) {
+                       Session session = null;
+                       try {
+                               Node profileNode = toDelete.get(username);
+                               userAdminService.deleteUser(username);
+                               profileNode.getParent().remove();
+                               session = profileNode.getSession();
+                               session.save();
+                       } catch (RepositoryException e) {
+                               JcrUtils.discardQuietly(session);
+                               throw new ArgeoException("Cannot list users", e);
+                       }
+               }
+
+               userAdminService.synchronize();
+               // UsersView view = (UsersView) HandlerUtil
+               // .getActiveWorkbenchWindow(event).getActivePage()
+               // .findView(UsersView.ID);
+               // view.refresh();
+               return null;
+       }
+
+       public void setUserAdminService(UserAdminService userAdminService) {
+               this.userAdminService = userAdminService;
+       }
+}
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/NewUser.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/NewUser.java
new file mode 100644 (file)
index 0000000..660896d
--- /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.security.ui.admin.commands;
+
+import javax.jcr.Repository;
+import javax.jcr.Session;
+
+import org.argeo.jcr.JcrUtils;
+import org.argeo.security.UserAdminService;
+import org.argeo.security.jcr.JcrSecurityModel;
+import org.argeo.security.ui.admin.wizards.NewUserWizard;
+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 that enables creation of a new user. */
+public class NewUser extends AbstractHandler {
+       private Repository repository;
+       private UserAdminService userAdminService;
+       private JcrSecurityModel jcrSecurityModel;
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               Session session = null;
+               try {
+                       session = repository.login();
+                       NewUserWizard newUserWizard = new NewUserWizard(session,
+                                       userAdminService, jcrSecurityModel);
+                       WizardDialog dialog = new WizardDialog(
+                                       HandlerUtil.getActiveShell(event), newUserWizard);
+                       dialog.open();
+               } catch (Exception e) {
+                       throw new ExecutionException("Cannot open wizard", e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+               }
+               return null;
+       }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setUserAdminService(UserAdminService userAdminService) {
+               this.userAdminService = userAdminService;
+       }
+
+       public void setJcrSecurityModel(JcrSecurityModel jcrSecurityModel) {
+               this.jcrSecurityModel = jcrSecurityModel;
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/OpenArgeoUserEditor.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/OpenArgeoUserEditor.java
new file mode 100644 (file)
index 0000000..bae928c
--- /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.commands;
+
+import org.argeo.security.ui.admin.editors.ArgeoUserEditor;
+import org.argeo.security.ui.admin.editors.ArgeoUserEditorInput;
+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.handlers.HandlerUtil;
+
+/** Command handler to set visible or open a Argeo user. */
+public class OpenArgeoUserEditor extends AbstractHandler {
+       public final static String COMMAND_ID = "org.argeo.security.ui.admin.openArgeoUserEditor";
+       public final static String PARAM_USERNAME = "org.argeo.security.ui.admin.username";
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               try {
+                       ArgeoUserEditorInput editorInput = new ArgeoUserEditorInput(
+                                       event.getParameter(PARAM_USERNAME));
+                       IWorkbenchPage activePage = HandlerUtil.getActiveWorkbenchWindow(
+                                       event).getActivePage();
+                       activePage.openEditor(editorInput, ArgeoUserEditor.ID);
+               } catch (Exception e) {
+                       throw new ExecutionException("Cannot open editor", e);
+               }
+               return null;
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/RefreshRoles.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/RefreshRoles.java
new file mode 100644 (file)
index 0000000..41c78f0
--- /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.security.ui.admin.commands;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+/**
+ * Refreshes the roles view.
+ */
+public class RefreshRoles extends AbstractHandler {
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+
+               return null;
+       }
+
+}
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/RefreshUsersList.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/RefreshUsersList.java
new file mode 100644 (file)
index 0000000..e4c14ab
--- /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.commands;
+
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.query.Query;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.security.UserAdminService;
+import org.argeo.security.ui.admin.views.UsersView;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * Refreshes the main user list, removing nodes which are not referenced by user
+ * admin service.
+ */
+public class RefreshUsersList extends AbstractHandler {
+       private UserAdminService userAdminService;
+       private Repository repository;
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               Set<String> users = userAdminService.listUsers();
+               Session session = null;
+               try {
+                       session = repository.login();
+                       Query query = session
+                                       .getWorkspace()
+                                       .getQueryManager()
+                                       .createQuery(
+                                                       "select * from [" + ArgeoTypes.ARGEO_USER_HOME
+                                                                       + "]", Query.JCR_SQL2);
+                       NodeIterator nit = query.execute().getNodes();
+                       while (nit.hasNext()) {
+                               Node node = nit.nextNode();
+                               String username = node.getProperty(ArgeoNames.ARGEO_USER_ID)
+                                               .getString();
+                               if (!users.contains(username))
+                                       node.remove();
+                       }
+                       session.save();
+               } catch (RepositoryException e) {
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoException("Cannot list users", e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+               }
+               userAdminService.synchronize();
+
+               // FIXME try to refresh views that extend the UsersView and have another
+               // ID
+               IWorkbenchPart part = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage().getActivePart();
+               if (part instanceof UsersView)
+                       ((UsersView) part).refresh();
+
+               // Try to refresh UsersView if opened
+               UsersView view = (UsersView) HandlerUtil
+                               .getActiveWorkbenchWindow(event).getActivePage()
+                               .findView(UsersView.ID);
+               if (view != null)
+                       view.refresh();
+
+               return null;
+       }
+
+       public void setUserAdminService(UserAdminService userAdminService) {
+               this.userAdminService = userAdminService;
+       }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+}
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/SaveArgeoUser.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/SaveArgeoUser.java
new file mode 100644 (file)
index 0000000..bd16f8b
--- /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.commands;
+
+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 COMMAND_ID = "org.argeo.security.ui.admin.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;
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/UserBatchUpdate.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/commands/UserBatchUpdate.java
new file mode 100644 (file)
index 0000000..657dfc7
--- /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.security.ui.admin.commands;
+
+import javax.jcr.Repository;
+import javax.jcr.Session;
+
+import org.argeo.jcr.JcrUtils;
+import org.argeo.security.UserAdminService;
+import org.argeo.security.jcr.JcrSecurityModel;
+import org.argeo.security.ui.admin.wizards.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 update various properties about users in JCR. */
+public class UserBatchUpdate extends AbstractHandler {
+       private Repository repository;
+       private UserAdminService userAdminService;
+       private JcrSecurityModel jcrSecurityModel;
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               Session session = null;
+               try {
+                       session = repository.login();
+                       UserBatchUpdateWizard userBatchUpdateWizard = new UserBatchUpdateWizard(session,
+                                       userAdminService, jcrSecurityModel);
+                       WizardDialog dialog = new WizardDialog(
+                                       HandlerUtil.getActiveShell(event), userBatchUpdateWizard);
+                       dialog.open();
+               } catch (Exception e) {
+                       throw new ExecutionException("Cannot open wizard", e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+               }
+               return null;
+       }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setUserAdminService(UserAdminService userAdminService) {
+               this.userAdminService = userAdminService;
+       }
+
+       public void setJcrSecurityModel(JcrSecurityModel jcrSecurityModel) {
+               this.jcrSecurityModel = jcrSecurityModel;
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/ArgeoUserEditor.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/ArgeoUserEditor.java
new file mode 100644 (file)
index 0000000..ebc0831
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * 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.editors;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.security.UserAdminService;
+import org.argeo.security.jcr.JcrUserDetails;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.argeo.security.ui.admin.views.UsersView;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.springframework.security.GrantedAuthority;
+
+/** Editor for an Argeo user. */
+public class ArgeoUserEditor extends FormEditor {
+       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
+                       + ".adminArgeoUserEditor";
+
+       /* DEPENDENCY INJECTION */
+       private Session session;
+       private UserAdminService userAdminService;
+
+       // private Node userHome;
+       private Node userProfile;
+       private JcrUserDetails userDetails;
+
+       public void init(IEditorSite site, IEditorInput input)
+                       throws PartInitException {
+               super.init(site, input);
+               String username = ((ArgeoUserEditorInput) getEditorInput())
+                               .getUsername();
+               userProfile = UserJcrUtils.getUserProfile(session, username);
+
+               if (userAdminService.userExists(username)) {
+                       userDetails = (JcrUserDetails) userAdminService
+                                       .loadUserByUsername(username);
+               } else {
+                       GrantedAuthority[] authorities = {};
+                       try {
+                               userDetails = new JcrUserDetails(session, username, null,
+                                               authorities);
+                       } catch (RepositoryException e) {
+                               throw new ArgeoException("Cannot retrieve disabled JCR profile");
+                       }
+               }
+
+               this.setPartProperty("name", username != null ? username : "<new user>");
+               setPartName(username != null ? username : "<new user>");
+       }
+
+       protected void addPages() {
+               try {
+                       addPage(new DefaultUserMainPage(this, userProfile));
+                       addPage(new UserRolesPage(this, userDetails, userAdminService));
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot add pages", e);
+               }
+       }
+
+       @Override
+       public void doSave(IProgressMonitor monitor) {
+               // list pages
+               // TODO: make it more generic
+               DefaultUserMainPage defaultUserMainPage = (DefaultUserMainPage) findPage(DefaultUserMainPage.ID);
+               if (defaultUserMainPage.isDirty()) {
+                       defaultUserMainPage.doSave(monitor);
+                       String newPassword = defaultUserMainPage.getNewPassword();
+                       defaultUserMainPage.resetNewPassword();
+                       if (newPassword != null)
+                               userDetails = userDetails.cloneWithNewPassword(newPassword);
+               }
+
+               UserRolesPage userRolesPage = (UserRolesPage) findPage(UserRolesPage.ID);
+               if (userRolesPage.isDirty()) {
+                       userRolesPage.doSave(monitor);
+                       userDetails = userDetails.cloneWithNewRoles(userRolesPage
+                                       .getRoles());
+               }
+
+               userAdminService.updateUser(userDetails);
+
+               // if (userAdminService.userExists(user.getUsername()))
+               // userAdminService.updateUser(user);
+               // else {
+               // userAdminService.newUser(user);
+               // setPartName(user.getUsername());
+               // }
+               firePropertyChange(PROP_DIRTY);
+
+               userRolesPage.setUserDetails(userDetails);
+
+               // FIXME rather use a refresh command. Fails when called by another
+               // view.
+               // refresh users view
+               IWorkbench iw = SecurityAdminPlugin.getDefault().getWorkbench();
+               UsersView usersView = (UsersView) iw.getActiveWorkbenchWindow()
+                               .getActivePage().findView(UsersView.ID);
+               if (usersView != null)
+                       usersView.refresh();
+       }
+
+       @Override
+       public void doSaveAs() {
+       }
+
+       @Override
+       public boolean isSaveAsAllowed() {
+               return false;
+       }
+
+       public void refresh() {
+               UserRolesPage userRolesPage = (UserRolesPage) findPage(UserRolesPage.ID);
+               userRolesPage.refresh();
+       }
+
+       @Override
+       public void dispose() {
+               JcrUtils.logoutQuietly(session);
+               super.dispose();
+       }
+       
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminService(UserAdminService userAdminService) {
+               this.userAdminService = userAdminService;
+       }
+       
+       public void setRepository(Repository repository) {
+               try {
+                       session = repository.login();
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Unable to initialise local session", re);
+               }
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/ArgeoUserEditorInput.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/ArgeoUserEditorInput.java
new file mode 100644 (file)
index 0000000..9d1d995
--- /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.editors;
+
+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/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/DefaultUserMainPage.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/DefaultUserMainPage.java
new file mode 100644 (file)
index 0000000..5f99e19
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * 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.editors;
+
+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.ArgeoException;
+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 ArgeoException("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 ArgeoException("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 ArgeoException("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 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/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/UserRolesPage.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/UserRolesPage.java
new file mode 100644 (file)
index 0000000..08cd457
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * 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.editors;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.security.UserAdminService;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.CheckboxCellEditor;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.EditingSupport;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.ui.forms.AbstractFormPart;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.FormPage;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.userdetails.UserDetails;
+
+/**
+ * Display/edit the roles of a user.
+ */
+public class UserRolesPage extends FormPage implements ArgeoNames {
+       final static String ID = "argeoUserEditor.rolesPage";
+
+       private final static Log log = LogFactory.getLog(UserRolesPage.class);
+       private final static Image ROLE_CHECKED = SecurityAdminPlugin
+                       .getImageDescriptor("icons/security.gif").createImage();
+
+       private TableViewer rolesViewer;
+       private UserAdminService userAdminService;
+       private List<String> roles;
+
+       public UserRolesPage(FormEditor editor, UserDetails userDetails,
+                       UserAdminService userAdminService) {
+               super(editor, ID, "Roles");
+               setUserDetails(userDetails);
+               this.userAdminService = userAdminService;
+       }
+
+       public void setUserDetails(UserDetails userDetails) {
+               this.roles = new ArrayList<String>();
+               for (GrantedAuthority ga : userDetails.getAuthorities())
+                       roles.add(ga.getAuthority());
+               if (rolesViewer != null)
+                       rolesViewer.refresh();
+       }
+
+       protected void createFormContent(final IManagedForm mf) {
+               ScrolledForm form = mf.getForm();
+               form.setText("Roles");
+               FillLayout mainLayout = new FillLayout();
+               // ColumnLayout mainLayout = new ColumnLayout();
+               // mainLayout.minNumColumns = 1;
+               // mainLayout.maxNumColumns = 4;
+               // mainLayout.topMargin = 0;
+               // mainLayout.bottomMargin = 5;
+               // mainLayout.leftMargin = mainLayout.rightMargin =
+               // mainLayout.horizontalSpacing = mainLayout.verticalSpacing = 10;
+               form.getBody().setLayout(mainLayout);
+               createRolesPart(form.getBody());
+       }
+
+       /** Creates the role section */
+       protected void createRolesPart(Composite parent) {
+               Table table = new Table(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+
+               AbstractFormPart part = new AbstractFormPart() {
+                       public void commit(boolean onSave) {
+                               // roles have already been modified in editing
+                               super.commit(onSave);
+                               if (log.isTraceEnabled())
+                                       log.trace("Role part committed");
+                       }
+               };
+               getManagedForm().addPart(part);
+
+               // GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
+               // gridData.verticalSpan = 20;
+               // table.setLayoutData(gridData);
+               table.setLinesVisible(true);
+               table.setHeaderVisible(false);
+               rolesViewer = new TableViewer(table);
+
+               // check column
+               TableViewerColumn column = createTableViewerColumn(rolesViewer,
+                               "checked", 20);
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       public String getText(Object element) {
+                               return null;
+                       }
+
+                       public Image getImage(Object element) {
+                               String role = element.toString();
+                               if (roles.contains(role)) {
+                                       return ROLE_CHECKED;
+                               } else {
+                                       return null;
+                               }
+                       }
+               });
+               column.setEditingSupport(new RoleEditingSupport(rolesViewer, part));
+
+               // role column
+               column = createTableViewerColumn(rolesViewer, "Role", 200);
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       public String getText(Object element) {
+                               return element.toString();
+                       }
+
+                       public Image getImage(Object element) {
+                               return null;
+                       }
+               });
+               rolesViewer.setContentProvider(new RolesContentProvider());
+               rolesViewer.setInput(getEditorSite());
+       }
+
+       protected TableViewerColumn createTableViewerColumn(TableViewer viewer,
+                       String title, int bound) {
+               final TableViewerColumn viewerColumn = new TableViewerColumn(viewer,
+                               SWT.NONE);
+               final TableColumn column = viewerColumn.getColumn();
+               column.setText(title);
+               column.setWidth(bound);
+               column.setResizable(true);
+               column.setMoveable(true);
+               return viewerColumn;
+
+       }
+
+       public List<String> getRoles() {
+               return roles;
+       }
+
+       public void refresh() {
+               rolesViewer.refresh();
+       }
+
+       private class RolesContentProvider implements IStructuredContentProvider {
+               public Object[] getElements(Object inputElement) {
+                       return userAdminService.listEditableRoles().toArray();
+               }
+
+               public void dispose() {
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+       }
+
+       /** Select the columns by editing the checkbox in the first column */
+       class RoleEditingSupport extends EditingSupport {
+
+               private final TableViewer viewer;
+               private final AbstractFormPart formPart;
+
+               public RoleEditingSupport(TableViewer viewer, AbstractFormPart formPart) {
+                       super(viewer);
+                       this.viewer = viewer;
+                       this.formPart = formPart;
+               }
+
+               @Override
+               protected CellEditor getCellEditor(Object element) {
+                       return new CheckboxCellEditor(null, SWT.CHECK | SWT.READ_ONLY);
+
+               }
+
+               @Override
+               protected boolean canEdit(Object element) {
+                       return true;
+               }
+
+               @Override
+               protected Object getValue(Object element) {
+                       String role = element.toString();
+                       return roles.contains(role);
+
+               }
+
+               @Override
+               protected void setValue(Object element, Object value) {
+                       Boolean inRole = (Boolean) value;
+                       String role = element.toString();
+                       if (inRole && !roles.contains(role)) {
+                               roles.add(role);
+                               formPart.markDirty();
+                       } else if (!inRole && roles.contains(role)) {
+                               roles.remove(role);
+                               formPart.markDirty();
+                       }
+                       viewer.refresh();
+               }
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/RolesView.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/RolesView.java
new file mode 100644 (file)
index 0000000..fbe7dd5
--- /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.security.ui.admin.views;
+
+import org.argeo.ArgeoException;
+import org.argeo.security.UserAdminService;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.argeo.security.ui.admin.commands.AddRole;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.handlers.IHandlerService;
+import org.eclipse.ui.part.ViewPart;
+
+/** List all roles. */
+public class RolesView extends ViewPart {
+       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
+                       + ".adminRolesView";
+
+       private Text newRole;
+
+       private TableViewer viewer;
+       private UserAdminService userAdminService;
+
+       private String addNewRoleText = "<add new role here>";
+
+       @Override
+       public void createPartControl(Composite parent) {
+               parent.setLayout(new GridLayout(1, false));
+
+               // new role text field
+               newRole = new Text(parent, SWT.BORDER);
+               newRole.setText(addNewRoleText);
+               newRole.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               // default action is add role
+               newRole.addListener(SWT.DefaultSelection, new Listener() {
+                       public void handleEvent(Event evt) {
+                               IWorkbench iw = SecurityAdminPlugin.getDefault().getWorkbench();
+                               IHandlerService handlerService = (IHandlerService) iw
+                                               .getService(IHandlerService.class);
+                               try {
+                                       handlerService.executeCommand(AddRole.COMMAND_ID, evt);
+                               } catch (Exception e) {
+                                       throw new ArgeoException("Cannot execute add role command",
+                                                       e);
+                               }
+                       }
+               });
+               // select all on focus
+               newRole.addListener(SWT.FocusIn, new Listener() {
+                       public void handleEvent(Event e) {
+                               newRole.selectAll();
+                       }
+               });
+
+               // roles table
+               Table table = new Table(parent, SWT.V_SCROLL | SWT.BORDER);
+               table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               table.setLinesVisible(false);
+               table.setHeaderVisible(false);
+               viewer = new TableViewer(table);
+               viewer.setContentProvider(new RolesContentProvider());
+               viewer.setLabelProvider(new UsersLabelProvider());
+               getViewSite().setSelectionProvider(viewer);
+               viewer.setInput(getViewSite());
+       }
+
+       @Override
+       public void setFocus() {
+               viewer.getTable().setFocus();
+       }
+
+       public void setUserAdminService(UserAdminService userAdminService) {
+               this.userAdminService = userAdminService;
+       }
+
+       public String getAddNewRoleText() {
+               return addNewRoleText;
+       }
+
+       private class RolesContentProvider implements IStructuredContentProvider {
+
+               public Object[] getElements(Object inputElement) {
+                       return userAdminService.listEditableRoles().toArray();
+               }
+
+               public void dispose() {
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+
+       }
+
+       private class UsersLabelProvider extends LabelProvider implements
+                       ITableLabelProvider {
+               public String getColumnText(Object element, int columnIndex) {
+                       return element.toString();
+               }
+
+               public Image getColumnImage(Object element, int columnIndex) {
+                       return null;
+               }
+
+       }
+
+       public String getNewRole() {
+               return newRole.getText();
+       }
+
+       public void refresh() {
+               viewer.refresh();
+               newRole.setText(addNewRoleText);
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/UsersView.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/views/UsersView.java
new file mode 100644 (file)
index 0000000..a743715
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * 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.views;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.utils.CommandUtils;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.argeo.security.ui.admin.UserTableComposite;
+import org.argeo.security.ui.admin.commands.OpenArgeoUserEditor;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.part.ViewPart;
+
+/** List all users with filter. */
+public class UsersView extends ViewPart implements ArgeoNames {
+       public final static String ID = SecurityAdminPlugin.PLUGIN_ID
+                       + ".adminUsersView";
+
+       /* DEPENDENCY INJECTION */
+       private Session session;
+
+       private UserTableComposite userTableCmp;
+       private JcrUserListener userStructureListener;
+       private JcrUserListener userPropertiesListener;
+
+       @Override
+       public void createPartControl(Composite parent) {
+               parent.setLayout(new FillLayout());
+
+               // Create the composite that displays the list and a filter
+               userTableCmp = new UserTableComposite(parent, SWT.NO_FOCUS, session);
+               userTableCmp.populate(true, false);
+
+               // Configure
+               userTableCmp.getTableViewer().addDoubleClickListener(
+                               new ViewDoubleClickListener());
+               getViewSite().setSelectionProvider(userTableCmp.getTableViewer());
+
+               // Add listener to refresh the list when something changes
+               userStructureListener = new JcrUserListener(getSite().getShell()
+                               .getDisplay());
+               JcrUtils.addListener(session, userStructureListener, Event.NODE_ADDED
+                               | Event.NODE_REMOVED, ArgeoJcrConstants.PEOPLE_BASE_PATH, null);
+               userPropertiesListener = new JcrUserListener(getSite().getShell()
+                               .getDisplay());
+               JcrUtils.addListener(session, userStructureListener,
+                               Event.PROPERTY_CHANGED | Event.PROPERTY_ADDED
+                                               | Event.PROPERTY_REMOVED,
+                               ArgeoJcrConstants.PEOPLE_BASE_PATH,
+                               ArgeoTypes.ARGEO_USER_PROFILE);
+       }
+
+       @Override
+       public void setFocus() {
+               userTableCmp.setFocus();
+       }
+
+       @Override
+       public void dispose() {
+               JcrUtils.removeListenerQuietly(session, userStructureListener);
+               JcrUtils.removeListenerQuietly(session, userPropertiesListener);
+               JcrUtils.logoutQuietly(session);
+               super.dispose();
+       }
+
+       // public void setSession(Session session) {
+       // this.session = session;
+       // }
+
+       public void refresh() {
+               this.getSite().getShell().getDisplay().asyncExec(new Runnable() {
+                       @Override
+                       public void run() {
+                               userTableCmp.refresh();
+                       }
+               });
+       }
+
+       private class JcrUserListener implements EventListener {
+               private final Display display;
+
+               public JcrUserListener(Display display) {
+                       super();
+                       this.display = display;
+               }
+
+               @Override
+               public void onEvent(EventIterator events) {
+                       display.asyncExec(new Runnable() {
+                               @Override
+                               public void run() {
+                                       userTableCmp.refresh();
+                               }
+                       });
+               }
+       }
+
+       class ViewDoubleClickListener implements IDoubleClickListener {
+               public void doubleClick(DoubleClickEvent evt) {
+                       if (evt.getSelection().isEmpty())
+                               return;
+
+                       Object obj = ((IStructuredSelection) evt.getSelection())
+                                       .getFirstElement();
+                       if (obj instanceof Node) {
+                               try {
+                                       String username = ((Node) obj).getProperty(ARGEO_USER_ID)
+                                                       .getString();
+                                       String commandId = OpenArgeoUserEditor.COMMAND_ID;
+                                       String paramName = OpenArgeoUserEditor.PARAM_USERNAME;
+                                       CommandUtils.callCommand(commandId, paramName, username);
+                               } catch (RepositoryException e) {
+                                       throw new ArgeoException("Cannot open user editor", e);
+                               }
+                       }
+               }
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setRepository(Repository repository) {
+               try {
+                       session = repository.login();
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Unable to initialise local session", re);
+               }
+       }
+
+}
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/MainUserInfoWizardPage.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/MainUserInfoWizardPage.java
new file mode 100644 (file)
index 0000000..ad3662b
--- /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.security.ui.admin.wizards;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.security.UserAdminService;
+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.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.UsernameNotFoundException;
+
+public class MainUserInfoWizardPage extends WizardPage implements
+               ModifyListener, ArgeoNames {
+       private Text username, firstName, lastName, primaryEmail, password1,
+                       password2;
+       private UserAdminService userAdminService;
+
+       public MainUserInfoWizardPage(UserAdminService userAdminService) {
+               super("Main");
+               this.userAdminService = userAdminService;
+               setTitle("Required Information");
+       }
+
+       @Override
+       public void createControl(Composite parent) {
+               Composite composite = new Composite(parent, SWT.NONE);
+               composite.setLayout(new GridLayout(2, false));
+               username = EclipseUiUtils.createGridLT(composite, "Username", this);
+               primaryEmail = EclipseUiUtils.createGridLT(composite, "Email", this);
+               firstName = EclipseUiUtils.createGridLT(composite, "First name", this);
+               lastName = EclipseUiUtils.createGridLT(composite, "Last name", this);
+               password1 = EclipseUiUtils.createGridLP(composite, "Password", this);
+               password2 = 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() {
+               // if (!username.getText().matches(UserAdminService.USERNAME_PATTERN))
+               // return
+               // "Wrong user name format, should be lower case, between 3 and 64 characters with only '_' an '@' as acceptable special character.";
+               
+               if (username.getText().trim().equals(""))
+                       return "User name must not be empty";
+               
+               try {
+                       UserDetails userDetails = userAdminService
+                                       .loadUserByUsername(username.getText());
+                       return "User " + userDetails.getUsername() + " already exists";
+               } catch (UsernameNotFoundException e) {
+                       // silent
+               }
+               if (!primaryEmail.getText().matches(UserAdminService.EMAIL_PATTERN))
+                       return "Not a valid email address";
+               if (firstName.getText().trim().equals(""))
+                       return "Specify a first name";
+               if (lastName.getText().trim().equals(""))
+                       return "Specify a last name";
+               if (password1.getText().trim().equals(""))
+                       return "Specify a password";
+               if (password2.getText().trim().equals(""))
+                       return "Repeat the password";
+               if (!password2.getText().equals(password1.getText()))
+                       return "Passwords are different";
+               return null;
+       }
+
+       public String getUsername() {
+               return username.getText();
+       }
+
+       public String getPassword() {
+               return password1.getText();
+       }
+
+       public void mapToProfileNode(Node up) {
+               try {
+                       up.setProperty(ARGEO_PRIMARY_EMAIL, primaryEmail.getText());
+                       up.setProperty(ARGEO_FIRST_NAME, firstName.getText());
+                       up.setProperty(ARGEO_LAST_NAME, lastName.getText());
+
+                       // derived values
+                       // TODO add wizard pages to do it
+                       up.setProperty(Property.JCR_TITLE, firstName.getText() + " "
+                                       + lastName.getText());
+                       up.setProperty(Property.JCR_DESCRIPTION, "");
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot map to " + up, e);
+               }
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/NewUserWizard.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/NewUserWizard.java
new file mode 100644 (file)
index 0000000..c2d041f
--- /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.wizards;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.security.UserAdminService;
+import org.argeo.security.jcr.JcrSecurityModel;
+import org.argeo.security.jcr.JcrUserDetails;
+import org.eclipse.jface.wizard.Wizard;
+import org.springframework.security.GrantedAuthority;
+
+/** Wizard to create a new user */
+public class NewUserWizard extends Wizard {
+       private final static Log log = LogFactory.getLog(NewUserWizard.class);
+       private Session session;
+       private UserAdminService userAdminService;
+       private JcrSecurityModel jcrSecurityModel;
+
+       // pages
+       private MainUserInfoWizardPage mainUserInfo;
+
+       public NewUserWizard(Session session, UserAdminService userAdminService,
+                       JcrSecurityModel jcrSecurityModel) {
+               this.session = session;
+               this.userAdminService = userAdminService;
+               this.jcrSecurityModel = jcrSecurityModel;
+       }
+
+       @Override
+       public void addPages() {
+               mainUserInfo = new MainUserInfoWizardPage(userAdminService);
+               addPage(mainUserInfo);
+       }
+
+       @Override
+       public boolean performFinish() {
+               if (!canFinish())
+                       return false;
+
+               String username = mainUserInfo.getUsername();
+               try {
+                       // Node userProfile = SecurityJcrUtils.createUserProfile(session,
+                       // username);
+                       Node userProfile = jcrSecurityModel.sync(session, username, null);
+                       session.getWorkspace().getVersionManager()
+                                       .checkout(userProfile.getPath());
+                       mainUserInfo.mapToProfileNode(userProfile);
+                       String password = mainUserInfo.getPassword();
+                       // TODO add roles
+                       JcrUserDetails jcrUserDetails = new JcrUserDetails(userProfile,
+                                       password, new GrantedAuthority[0]);
+                       session.save();
+                       session.getWorkspace().getVersionManager()
+                                       .checkin(userProfile.getPath());
+                       userAdminService.createUser(jcrUserDetails);
+                       return true;
+               } catch (Exception e) {
+                       JcrUtils.discardQuietly(session);
+                       Node userHome = UserJcrUtils.getUserHome(session, username);
+                       if (userHome != null) {
+                               try {
+                                       userHome.remove();
+                                       session.save();
+                               } catch (RepositoryException e1) {
+                                       JcrUtils.discardQuietly(session);
+                                       log.warn("Error when trying to clean up failed new user "
+                                                       + username, e1);
+                               }
+                       }
+                       ErrorFeedback.show("Cannot create new user " + username, e);
+                       return false;
+               }
+       }
+
+       public void setSession(Session session) {
+               this.session = session;
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/UserBatchUpdateWizard.java b/trunk/security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/UserBatchUpdateWizard.java
new file mode 100644 (file)
index 0000000..71e4a1b
--- /dev/null
@@ -0,0 +1,607 @@
+/*
+ * 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.wizards;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.version.VersionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.ArgeoMonitor;
+import org.argeo.eclipse.ui.EclipseArgeoMonitor;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.security.UserAdminService;
+import org.argeo.security.jcr.JcrSecurityModel;
+import org.argeo.security.jcr.JcrUserDetails;
+import org.argeo.security.ui.PrivilegedJob;
+import org.argeo.security.ui.admin.SecurityAdminPlugin;
+import org.argeo.security.ui.admin.UserTableComposite;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+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.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.FillLayout;
+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.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Wizard to update users */
+public class UserBatchUpdateWizard extends Wizard {
+       private final static Log log = LogFactory
+                       .getLog(UserBatchUpdateWizard.class);
+       private Session session;
+       private UserAdminService userAdminService;
+
+       // pages
+       private ChooseCommandWizardPage chooseCommandPage;
+       private ChooseUsersWizardPage userListPage;
+       private ValidateAndLaunchWizardPage validatePage;
+
+       // /////////////////////////////////////////////////
+       // / Definition of the various implemented commands
+       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("Enable user(s)", ArgeoNames.ARGEO_ENABLED);
+                       put("Expire credentials", ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED);
+                       put("Expire account(s)", ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED);
+                       put("Lock account(s)", ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED);
+                       put("Reset password(s)", CMD_UPDATE_PASSWORD);
+                       // TODO implement role / group management
+                       // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP);
+               }
+       };
+
+       public UserBatchUpdateWizard(Session session,
+                       UserAdminService userAdminService, JcrSecurityModel jcrSecurityModel) {
+               this.session = session;
+               this.userAdminService = userAdminService;
+               // this.jcrSecurityModel = jcrSecurityModel;
+       }
+
+       @Override
+       public void addPages() {
+               chooseCommandPage = new ChooseCommandWizardPage();
+               addPage(chooseCommandPage);
+               userListPage = new ChooseUsersWizardPage(session);
+               addPage(userListPage);
+               validatePage = new ValidateAndLaunchWizardPage(session);
+               addPage(validatePage);
+       }
+
+       @Override
+       public boolean performFinish() {
+               if (!canFinish())
+                       return false;
+
+               UpdateJob job = null;
+               if (ArgeoNames.ARGEO_ENABLED.equals(chooseCommandPage.getCommand())) {
+                       job = new UpdateBoolean(session, userListPage.getSelectedUsers(),
+                                       ArgeoNames.ARGEO_ENABLED,
+                                       chooseCommandPage.getBoleanValue());
+               } else if (ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED
+                               .equals(chooseCommandPage.getCommand())) {
+                       job = new UpdateBoolean(session, userListPage.getSelectedUsers(),
+                                       ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED,
+                                       chooseCommandPage.getBoleanValue());
+               } else if (ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED
+                               .equals(chooseCommandPage.getCommand())) {
+                       job = new UpdateBoolean(session, userListPage.getSelectedUsers(),
+                                       ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED,
+                                       chooseCommandPage.getBoleanValue());
+               } else if (ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED.equals(chooseCommandPage
+                               .getCommand())) {
+                       job = new UpdateBoolean(session, userListPage.getSelectedUsers(),
+                                       ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED,
+                                       chooseCommandPage.getBoleanValue());
+               } else if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) {
+                       String newValue = chooseCommandPage.getPwdValue();
+                       if (newValue == null)
+                               throw new ArgeoException(
+                                               "Password cannot be null or an empty string");
+                       job = new ResetPassword(session, userAdminService,
+                                       userListPage.getSelectedUsers(), newValue);
+               }
+
+               if (job != null)
+                       job.schedule();
+               return true;
+       }
+
+       public void setSession(Session session) {
+               this.session = session;
+       }
+
+       public boolean canFinish() {
+               if (this.getContainer().getCurrentPage() == validatePage)
+                       return true;
+               return false;
+       }
+
+       // /////////////////////////
+       // REEL UPDATE JOB
+       private class UpdateBoolean extends UpdateJob {
+               private String propertyName;
+               private boolean value;
+
+               public UpdateBoolean(Session session, List<Node> nodesToUpdate,
+                               String propertyName, boolean value) {
+                       super(session, nodesToUpdate);
+                       this.propertyName = propertyName;
+                       this.value = value;
+               }
+
+               protected void doUpdate(Node node) {
+                       try {
+                               node.setProperty(propertyName, value);
+                       } catch (RepositoryException re) {
+                               throw new ArgeoException(
+                                               "Unable to update boolean value for node " + node, re);
+                       }
+               }
+       }
+
+       private class ResetPassword extends UpdateJob {
+               private String newValue;
+               private UserAdminService userAdminService;
+
+               public ResetPassword(Session session,
+                               UserAdminService userAdminService, List<Node> nodesToUpdate,
+                               String newValue) {
+                       super(session, nodesToUpdate);
+                       this.newValue = newValue;
+                       this.userAdminService = userAdminService;
+               }
+
+               protected void doUpdate(Node node) {
+                       try {
+                               String userId = node.getProperty(ArgeoNames.ARGEO_USER_ID)
+                                               .getString();
+                               if (userAdminService.userExists(userId)) {
+                                       JcrUserDetails userDetails = (JcrUserDetails) userAdminService
+                                                       .loadUserByUsername(userId);
+                                       userAdminService.updateUser(userDetails
+                                                       .cloneWithNewPassword(newValue));
+                               }
+                       } catch (RepositoryException re) {
+                               throw new ArgeoException(
+                                               "Unable to update boolean value for node " + node, re);
+                       }
+               }
+       }
+
+       @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 ArgeoException("Not yet implemented");
+                       // } catch (RepositoryException re) {
+                       // throw new ArgeoException(
+                       // "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 Session currSession;
+               private final List<Node> nodesToUpdate;
+
+               protected abstract void doUpdate(Node node);
+
+               public UpdateJob(Session session, List<Node> nodesToUpdate) {
+                       super("Perform update");
+                       try {
+                               this.currSession = session.getRepository().login();
+                               // "move" nodes to update in the new session
+                               // the "old" session will be closed by the calling command
+                               // before the job has effectively ran
+                               // TODO there must be a cleaner way to do.
+                               List<Node> nodes = new ArrayList<Node>();
+                               for (Node node : nodesToUpdate) {
+                                       nodes.add(currSession.getNode(node.getPath()));
+                               }
+                               this.nodesToUpdate = nodes;
+                       } catch (RepositoryException e) {
+                               throw new ArgeoException("Error while dupplicating "
+                                               + "session for job", e);
+                       }
+               }
+
+               @Override
+               protected IStatus doRun(IProgressMonitor progressMonitor) {
+                       try {
+                               ArgeoMonitor monitor = new EclipseArgeoMonitor(progressMonitor);
+                               VersionManager vm = currSession.getWorkspace()
+                                               .getVersionManager();
+                               int total = nodesToUpdate.size();
+                               monitor.beginTask("Performing change", total);
+                               for (Node node : nodesToUpdate) {
+                                       String path = node.getPath();
+                                       vm.checkout(path);
+                                       doUpdate(node);
+                                       currSession.save();
+                                       vm.checkin(path);
+                                       monitor.worked(1);
+                               }
+                       } catch (Exception e) {
+                               log.error("Cannot perform batch update on users", e);
+                               // e.printStackTrace();
+
+                               // Dig exception to find the root cause that will enable the
+                               // user to understand the problem
+                               Throwable cause = e;
+                               Throwable originalCause = e;
+                               while (cause != null) {
+                                       if (log.isTraceEnabled())
+                                               log.trace("Parent Cause message : "
+                                                               + cause.getMessage());
+                                       originalCause = cause;
+                                       cause = cause.getCause();
+                               }
+                               return new Status(IStatus.ERROR, SecurityAdminPlugin.PLUGIN_ID,
+                                               "Cannot perform updates.", originalCause);
+                       } finally {
+                               JcrUtils.logoutQuietly(currSession);
+                       }
+                       return Status.OK_STATUS;
+               }
+       }
+
+       // //////////////////////
+       // Pages definition
+       /** Displays a combo box that enables user to choose which action to perform */
+       private class ChooseCommandWizardPage extends WizardPage {
+               private static final long serialVersionUID = 1L;
+
+               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.NO_FOCUS);
+                       String[] values = commands.keySet().toArray(
+                                       new String[commands.size()]);
+                       chooseCommandCmb.setItems(values);
+                       chooseCommandCmb.setLayoutData(new GridData(SWT.FILL, SWT.TOP,
+                                       true, false));
+
+                       final Composite bottomPart = new Composite(container, SWT.NO_FOCUS);
+                       gl = new GridLayout();
+                       gl.horizontalSpacing = gl.marginWidth = gl.verticalSpacing = 0;
+                       bottomPart.setLayout(gl);
+                       bottomPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
+                                       true));
+
+                       chooseCommandCmb.addSelectionListener(new SelectionListener() {
+                               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);
+                                       bottomPart.pack(true);
+                                       bottomPart.layout();
+                               }
+
+                               @Override
+                               public void widgetDefaultSelected(SelectionEvent e) {
+                               }
+                       });
+
+                       setControl(container);
+               }
+
+               private void cleanParent(Composite parent) {
+                       if (parent.getChildren().length > 0) {
+                               for (Control control : parent.getChildren())
+                                       control.dispose();
+                       }
+               }
+
+               private void populateBooleanFlagCmp(Composite parent) {
+                       cleanParent(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) {
+                       cleanParent(parent);
+                       Composite body = new Composite(parent, SWT.NO_FOCUS);
+                       body.setLayout(new GridLayout(2, false));
+                       body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       pwdTxt = createLP(body, "New password", "");
+                       pwd2Txt = createLP(body, "Repeat password", "");
+               }
+
+               /** Creates label and password. */
+               protected Text createLP(Composite body, String label, String value) {
+                       Label lbl = new Label(body, SWT.NONE);
+                       lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+                       lbl.setText(label);
+                       Text text = new Text(body, SWT.BORDER | SWT.PASSWORD);
+                       text.setText(value);
+                       text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+                       return text;
+               }
+
+               private void populateGroupCmp(Composite parent) {
+                       if (parent.getChildren().length > 0) {
+                               for (Control control : parent.getChildren())
+                                       control.dispose();
+                       }
+                       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());
+               }
+
+               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 String getPwdValue() {
+                       String newPwd = null;
+                       if (pwdTxt == null || pwd2Txt == null)
+                               return null;
+                       if (!pwdTxt.getText().equals("") || !pwd2Txt.getText().equals("")) {
+                               if (pwdTxt.getText().equals(pwd2Txt.getText())) {
+                                       newPwd = pwdTxt.getText();
+                                       pwdTxt.setText("");
+                                       pwd2Txt.setText("");
+                               } else {
+                                       pwdTxt.setText("");
+                                       pwd2Txt.setText("");
+                                       throw new ArgeoException("Passwords are not equals");
+                               }
+                       }
+                       return newPwd;
+               }
+       }
+
+       /**
+        * 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 = 1L;
+               private UserTableComposite userTableCmp;
+               private Composite container;
+               private Session session;
+
+               public ChooseUsersWizardPage(Session session) {
+                       super("Choose Users");
+                       this.session = session;
+                       setTitle("Select users who will be impacted");
+               }
+
+               @Override
+               public void createControl(Composite parent) {
+                       container = new Composite(parent, SWT.NONE);
+                       container.setLayout(new FillLayout());
+                       userTableCmp = new MyUserTableCmp(container, SWT.NO_FOCUS, session);
+                       userTableCmp.populate(true, true);
+                       setControl(container);
+
+                       // Add listener to update message when shown
+                       final IWizardContainer container = this.getContainer();
+                       if (container instanceof IPageChangeProvider) {
+                               ((IPageChangeProvider) container).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<Node> getSelectedUsers() {
+                       return userTableCmp.getSelectedUsers();
+               }
+
+               private class MyUserTableCmp extends UserTableComposite {
+
+                       private static final long serialVersionUID = 1L;
+
+                       public MyUserTableCmp(Composite parent, int style, Session session) {
+                               super(parent, style, session);
+                       }
+
+                       @Override
+                       protected void refreshFilteredList() {
+                               List<Node> nodes = new ArrayList<Node>();
+                               try {
+                                       NodeIterator ni = listFilteredElements(session,
+                                                       getFilterString());
+
+                                       users: while (ni.hasNext()) {
+                                               Node currNode = ni.nextNode();
+                                               String username = currNode.hasProperty(ARGEO_USER_ID) ? currNode
+                                                               .getProperty(ARGEO_USER_ID).getString() : "";
+                                               if (username.equals(session.getUserID()))
+                                                       continue users;
+                                               else
+                                                       nodes.add(currNode);
+                                       }
+                                       getTableViewer().setInput(nodes.toArray());
+                               } catch (RepositoryException e) {
+                                       throw new ArgeoException("Unable to list users", e);
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Recapitulation of input data before running real update
+        */
+       private class ValidateAndLaunchWizardPage extends WizardPage implements
+                       IPageChangedListener {
+               private static final long serialVersionUID = 1L;
+               private UserTableComposite userTableCmp;
+               private Session session;
+
+               public ValidateAndLaunchWizardPage(Session session) {
+                       super("Validate and launch");
+                       this.session = session;
+                       setTitle("Validate and launch");
+               }
+
+               @Override
+               public void createControl(Composite parent) {
+                       Composite mainCmp = new Composite(parent, SWT.NO_FOCUS);
+                       mainCmp.setLayout(new FillLayout());
+
+                       // Add listener to update user list when shown
+                       final IWizardContainer container = this.getContainer();
+                       if (container instanceof IPageChangeProvider) {
+                               ((IPageChangeProvider) container).addPageChangedListener(this);
+                       }
+
+                       userTableCmp = new UserTableComposite(mainCmp, SWT.NO_FOCUS,
+                                       session);
+                       userTableCmp.populate(false, false);
+                       setControl(mainCmp);
+               }
+
+               @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?";
+                               ((WizardPage) event.getSelectedPage()).setMessage(msg);
+                       }
+               }
+
+               // private class MyUserTableCmp extends UserTableComposite {
+               // public MyUserTableCmp(Composite parent, int style, Session session) {
+               // super(parent, style, session);
+               // }
+               //
+               // @Override
+               // protected void refreshFilteredList() {
+               // @SuppressWarnings({ "unchecked", "rawtypes" })
+               //
+               // setFilteredList(values);
+               // }
+               //
+               // @Override
+               // public void setVisible(boolean visible) {
+               // super.setVisible(visible);
+               // if (visible)
+               // refreshFilteredList();
+               // }
+               // }
+       }
+}
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/.classpath b/trunk/security/plugins/org.argeo.security.ui.rap/.classpath
new file mode 100644 (file)
index 0000000..5641c7c
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/.project b/trunk/security/plugins/org.argeo.security.ui.rap/.project
new file mode 100644 (file)
index 0000000..d20bec7
--- /dev/null
@@ -0,0 +1,28 @@
+<?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/trunk/security/plugins/org.argeo.security.ui.rap/META-INF/jaas_default.txt b/trunk/security/plugins/org.argeo.security.ui.rap/META-INF/jaas_default.txt
new file mode 100644 (file)
index 0000000..c74797b
--- /dev/null
@@ -0,0 +1,23 @@
+UNIX {
+    org.eclipse.equinox.security.auth.module.ExtensionLoginModule sufficient
+        extensionId="org.argeo.security.equinox.unixLoginModule";
+};
+
+SPRING {
+    org.eclipse.equinox.security.auth.module.ExtensionLoginModule sufficient
+        extensionId="org.argeo.security.equinox.springLoginModule";
+};
+
+SPRING_ANONYMOUS {
+    org.eclipse.equinox.security.auth.module.ExtensionLoginModule sufficient
+        extensionId="org.argeo.security.equinox.anonymousSpringLoginModule";
+};
+
+SPRING_SECURITY_CONTEXT {
+    org.eclipse.equinox.security.auth.module.ExtensionLoginModule sufficient
+        extensionId="org.argeo.security.equinox.springSecurityContextLoginModule";
+};
+
+KEYRING {
+    org.argeo.security.crypto.KeyringLoginModule required;
+};
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/META-INF/spring/commands.xml b/trunk/security/plugins/org.argeo.security.ui.rap/META-INF/spring/commands.xml
new file mode 100644 (file)
index 0000000..3dd037a
--- /dev/null
@@ -0,0 +1,22 @@
+<?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="userDetailsManager" ref="userDetailsManager" />
+       </bean>
+
+
+       <!-- RAP Specific command and corresponding service to enable open file -->
+       <bean id="org.argeo.security.ui.specific.openFile" class="org.argeo.eclipse.ui.specific.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/trunk/security/plugins/org.argeo.security.ui.rap/META-INF/spring/osgi.xml b/trunk/security/plugins/org.argeo.security.ui.rap/META-INF/spring/osgi.xml
new file mode 100644 (file)
index 0000000..9e357a3
--- /dev/null
@@ -0,0 +1,14 @@
+<?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="userDetailsManager"\r
+               interface="org.springframework.security.userdetails.UserDetailsManager"\r
+               cardinality="0..1" />\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/branding/afterLogout.html b/trunk/security/plugins/org.argeo.security.ui.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/trunk/security/plugins/org.argeo.security.ui.rap/branding/empty.html b/trunk/security/plugins/org.argeo.security.ui.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/trunk/security/plugins/org.argeo.security.ui.rap/branding/favicon.ico b/trunk/security/plugins/org.argeo.security.ui.rap/branding/favicon.ico
new file mode 100644 (file)
index 0000000..213cdf7
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.rap/branding/favicon.ico differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/branding/login.html b/trunk/security/plugins/org.argeo.security.ui.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/trunk/security/plugins/org.argeo.security.ui.rap/branding/public.html b/trunk/security/plugins/org.argeo.security.ui.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/trunk/security/plugins/org.argeo.security.ui.rap/build.properties b/trunk/security/plugins/org.argeo.security.ui.rap/build.properties
new file mode 100644 (file)
index 0000000..5618fae
--- /dev/null
@@ -0,0 +1,6 @@
+bin.includes = plugin.xml,\
+               META-INF/,\
+               branding/,\
+               icons/
+source.. = src/main/java/
+output.. = target/classes/
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/icons/closeAll.gif b/trunk/security/plugins/org.argeo.security.ui.rap/icons/closeAll.gif
new file mode 100644 (file)
index 0000000..28a3785
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.rap/icons/closeAll.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/icons/exit.png b/trunk/security/plugins/org.argeo.security.ui.rap/icons/exit.png
new file mode 100644 (file)
index 0000000..cfbf9d1
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.rap/icons/exit.png differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/icons/home.gif b/trunk/security/plugins/org.argeo.security.ui.rap/icons/home.gif
new file mode 100644 (file)
index 0000000..fd0c669
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.rap/icons/home.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/icons/main.gif b/trunk/security/plugins/org.argeo.security.ui.rap/icons/main.gif
new file mode 100644 (file)
index 0000000..90a0014
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.rap/icons/main.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/icons/password.gif b/trunk/security/plugins/org.argeo.security.ui.rap/icons/password.gif
new file mode 100644 (file)
index 0000000..a6b251f
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.rap/icons/password.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/icons/preferences.png b/trunk/security/plugins/org.argeo.security.ui.rap/icons/preferences.png
new file mode 100644 (file)
index 0000000..aa0dc0b
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.rap/icons/preferences.png differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/plugin.xml b/trunk/security/plugins/org.argeo.security.ui.rap/plugin.xml
new file mode 100644 (file)
index 0000000..49506de
--- /dev/null
@@ -0,0 +1,222 @@
+<?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.SecureEntryPoint"
+            path="/node"
+            brandingId="org.argeo.security.ui.rap.defaultBranding">
+      </entrypoint>
+      <entrypoint
+            id="org.argeo.security.ui.rap.secureEntryPoint"
+            class="org.argeo.security.ui.rap.SecureEntryPoint"
+            path="/secureWebUi"
+            brandingId="org.argeo.security.ui.rap.defaultBranding">
+      </entrypoint>
+      <entrypoint
+            id="org.argeo.security.ui.rap.anonymousEntryPoint"
+            class="org.argeo.security.ui.rap.AnonymousEntryPoint"
+            path="/publicWebUi"
+            brandingId="org.argeo.security.ui.rap.defaultBranding">
+      </entrypoint>
+      <entrypoint
+            id="org.argeo.security.ui.rap.logoutEntryPoint"
+            class="org.argeo.security.ui.rap.LogoutEntryPoint"
+            path="/logout"
+            brandingId="org.argeo.security.ui.rap.logoutBranding">
+      </entrypoint>
+      <entrypoint
+            id="org.argeo.security.ui.rap.nullEntryPoint"
+            class="org.argeo.security.ui.rap.NullEntryPoint"
+            path="/nullEP">
+      </entrypoint>
+   </extension>
+
+       <extension
+         point="org.eclipse.rap.ui.branding">
+       <!-- themeId attribute is by default set to this value.
+                       Left anyway to provide the pattern when defining a later 
+                       own default theme for Argeo Rap UIs. 
+                       corresponding theme is found in spite of the warning -->
+               
+       <!--
+       body="branding/login.html" 
+       body attribute pointing to an auto refresh page triggers weird side effects: 
+       the whole workbench is re-loaded (like pressing on F5) every now and then. 
+       Must be checked. Removed from the time being.-->   
+               <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>
+       
+       <!-- LOCAL THEMES - Use the following to extend or overwrite default theme --> 
+       <!--    <extension
+               point="org.eclipse.rap.ui.themes">
+               <themeContribution
+                       themeId="org.eclipse.rap.rwt.theme.Default"
+                       file="theme/defaultExt.css" /> 
+       </extension> -->
+
+       <extension
+               point="org.eclipse.equinox.security.callbackHandlerMapping">
+               <callbackHandlerMapping
+            callbackHandlerId="org.argeo.security.ui.defaultLoginDialog"
+            configName="SPRING">
+               </callbackHandlerMapping>
+       </extension>
+       <extension
+               point="org.eclipse.equinox.security.callbackHandlerMapping">
+               <callbackHandlerMapping
+                       callbackHandlerId="org.argeo.security.ui.defaultLoginDialog"
+                       configName="NIX">
+               </callbackHandlerMapping>
+       </extension>
+       <extension
+         point="org.eclipse.equinox.security.callbackHandlerMapping">
+      <callbackHandlerMapping
+            callbackHandlerId="org.argeo.security.ui.defaultLoginDialog"
+            configName="SPRING_SECURITY_CONTEXT">
+      </callbackHandlerMapping>
+   </extension>
+
+  <extension point="org.eclipse.ui.menus">
+    <!--       <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/main.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>
+               <menuContribution locationURI="menu:org.argeo.security.ui.rap.mainMenu">
+        <command
+              commandId="org.argeo.security.ui.rap.userMenuCommand"
+              icon="icons/home.gif"
+              id="org.argeo.security.ui.rap.userMenu">
+        </command>
+        <command
+              commandId="org.eclipse.ui.window.preferences"
+              icon="icons/preferences.png"/>
+        <command
+              commandId="org.argeo.security.ui.rap.openChangePasswordDialog"
+              icon="icons/password.gif"
+              label="Change password"/>
+        <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"/>
+        <separator
+              name="org.argeo.security.ui.rap.beforeExit"
+              visible="true">
+        </separator>-->
+        <!--<command commandId="org.eclipse.ui.views.showView"/>-->
+        <!--<command commandId="org.eclipse.ui.perspectives.showPerspective"/>-->
+        <!-- <command
+              commandId="org.eclipse.ui.file.exit"
+              icon="icons/exit.png"/>
+     </menuContribution> -->
+  </extension>
+
+       <!-- COMMANDS --> 
+       <extension point="org.eclipse.ui.commands">
+               <command
+                       id="org.argeo.security.ui.rap.openChangePasswordDialog"
+                       defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+                       name="Change Password">
+               </command>
+               <command
+                       id="org.argeo.security.ui.rap.mainMenuCommand"
+                       defaultHandler="org.argeo.security.ui.commands.OpenHomePerspective"
+                       name="Main">
+               </command>
+               
+       <!-- Register a default command that enable an "open file" action in a single sourced application  -->  
+               <command
+                       defaultHandler="org.argeo.eclipse.spring.SpringExtensionFactory"
+                       id="org.argeo.security.ui.specific.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>
+       <!-- SERVICE HANDLERS --> 
+       <extension point="org.eclipse.rap.ui.serviceHandler">
+               <!-- The required rap specific handler to call the open file command over the internet-->
+               <serviceHandler
+                       class="org.argeo.eclipse.ui.specific.OpenFileService"
+                       id="org.argeo.security.ui.specific.openFileService">
+               </serviceHandler>
+       </extension>
+    
+       <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="ROLE_ANONYMOUS" />
+                     </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="ROLE_ANONYMOUS" />
+                     </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>
+</plugin>
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/pom.xml b/trunk/security/plugins/org.argeo.security.ui.rap/pom.xml
new file mode 100644 (file)
index 0000000..79e4022
--- /dev/null
@@ -0,0 +1,83 @@
+<?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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>plugins</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.ui.rap</artifactId>
+       <name>Commons Security UI RAP</name>
+       <packaging>jar</packaging>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-Activator>org.argeo.security.ui.rap.SecureRapActivator</Bundle-Activator>
+                                               <Bundle-ActivationPolicy>lazy</Bundle-ActivationPolicy>
+                                               <Require-Bundle>org.eclipse.rap.ui,org.eclipse.core.runtime</Require-Bundle>
+                                               <Import-Package>
+                                                               org.springframework.core,
+                                                               org.argeo.eclipse.spring,
+                                                               org.argeo.eclipse.ui.specific,
+                                                               *
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- Commons -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+
+               <!-- Argeo Security -->
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.ui</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.equinox</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- RAP specific -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.dep.rap</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui.rap</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/AnonymousEntryPoint.java b/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/AnonymousEntryPoint.java
new file mode 100644 (file)
index 0000000..16d2489
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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.LoginException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.eclipse.equinox.security.auth.ILoginContext;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.application.IEntryPoint;
+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 IEntryPoint {
+       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 loginTimeout = 1 * 60;
+       private Integer sessionTimeout = 15 * 60;
+
+       @Override
+       public int createUI() {
+               // Short login timeout so that the modal dialog login doesn't hang
+               // around too long
+               RWT.getRequest().getSession().setMaxInactiveInterval(loginTimeout);
+
+               if (log.isDebugEnabled())
+                       log.debug("Anonymous THREAD=" + Thread.currentThread().getId()
+                                       + ", sessionStore=" + RWT.getSessionStore().getId());
+
+               // create display
+               final Display display = PlatformUI.createDisplay();
+
+               // log in
+               final ILoginContext loginContext = SecureRapActivator
+                               .createLoginContext(SecureRapActivator.CONTEXT_SPRING_ANONYMOUS);
+               Subject subject = null;
+               try {
+                       loginContext.login();
+                       subject = loginContext.getSubject();
+               } catch (LoginException e) {
+                       throw new ArgeoException(
+                                       "Unexpected exception during authentication", e);
+               }
+
+               // identify after successful login
+               if (log.isDebugEnabled())
+                       log.debug("Authenticated " + subject);
+               final String username = subject.getPrincipals().iterator().next()
+                               .getName();
+
+               // Once the user is logged in, she can have a longer session timeout
+               RWT.getRequest().getSession().setMaxInactiveInterval(sessionTimeout);
+
+               // 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);
+               } finally {
+                       display.dispose();
+               }
+               return returnCode;
+       }
+
+       private void logout(ILoginContext secureContext, String username) {
+               try {
+                       secureContext.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/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/LogoutEntryPoint.java b/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/LogoutEntryPoint.java
new file mode 100644 (file)
index 0000000..7337549
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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 javax.security.auth.login.LoginException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.equinox.security.auth.ILoginContext;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.application.IEntryPoint;
+import org.eclipse.ui.PlatformUI;
+import org.springframework.security.context.SecurityContextHolder;
+
+/**
+ * RAP entry point which logs out the currently authenticated user
+ */
+public class LogoutEntryPoint implements IEntryPoint {
+       private final static Log log = LogFactory.getLog(LogoutEntryPoint.class);
+
+       /**
+        * From org.springframework.security.context.
+        * HttpSessionContextIntegrationFilter
+        */
+       protected static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
+
+       @Override
+       public int createUI() {
+               // create display
+               PlatformUI.createDisplay();
+
+               final ILoginContext loginContext = SecureRapActivator
+                               .createLoginContext(SecureRapActivator.CONTEXT_SPRING);
+               try {
+                       loginContext.logout();
+               } catch (LoginException e) {
+                       e.printStackTrace();
+               }
+
+               RWT.getRequest().getSession()
+                               .removeAttribute(SPRING_SECURITY_CONTEXT_KEY);
+               SecurityContextHolder.clearContext();
+               RWT.getRequest().getSession().setMaxInactiveInterval(1);
+
+               if (log.isDebugEnabled())
+                       log.debug("Logged out session " + RWT.getSessionStore().getId());
+               return 0;
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/NullEntryPoint.java b/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/NullEntryPoint.java
new file mode 100644 (file)
index 0000000..811cc28
--- /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.security.ui.rap;
+
+import org.eclipse.rap.rwt.application.IEntryPoint;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * RAP entry point which does doesing except creating the display
+ */
+public class NullEntryPoint implements IEntryPoint {
+       @Override
+       public int createUI() {
+               // create display
+               PlatformUI.createDisplay();
+               return 0;
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/RapActionBarAdvisor.java b/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/RapActionBarAdvisor.java
new file mode 100644 (file)
index 0000000..074b798
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * 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.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) {
+               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(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/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/RapWindowAdvisor.java b/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/RapWindowAdvisor.java
new file mode 100644 (file)
index 0000000..4808152
--- /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.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;
+import org.eclipse.ui.internal.UIPlugin;
+
+/** 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() {
+                       @Override
+                       public void handleEvent(Event event) {
+                               Rectangle bounds = event.display.getBounds();
+                               IWorkbenchWindow iww = UIPlugin.getDefault().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/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/RapWorkbenchAdvisor.java b/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/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/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/SecureEntryPoint.java b/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/SecureEntryPoint.java
new file mode 100644 (file)
index 0000000..288ca62
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * 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.LoginException;
+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.ArgeoException;
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.util.LocaleUtils;
+import org.eclipse.equinox.security.auth.ILoginContext;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.application.IEntryPoint;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
+
+/**
+ * RAP entry point with login capabilities. Once the user has been
+ * authenticated, the workbench is run as a privileged action by the related
+ * subject.
+ */
+public class SecureEntryPoint implements IEntryPoint {
+       private final static Log log = LogFactory.getLog(SecureEntryPoint.class);
+
+       /**
+        * From org.springframework.security.context.
+        * HttpSessionContextIntegrationFilter
+        */
+       protected static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
+
+       /**
+        * 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);
+
+               // Try to load security context thanks to the session processing filter
+               HttpServletRequest httpRequest = RWT.getRequest();
+               HttpSession httpSession = httpRequest.getSession();
+               Object contextFromSessionObject = httpSession
+                               .getAttribute(SPRING_SECURITY_CONTEXT_KEY);
+               if (contextFromSessionObject != null)
+                       SecurityContextHolder
+                                       .setContext((SecurityContext) contextFromSessionObject);
+
+//             if (log.isDebugEnabled())
+//                     log.debug("THREAD=" + Thread.currentThread().getId()
+//                                     + ", sessionStore=" + RWT.getSessionStore().getId()
+//                                     + ", remote user=" + httpRequest.getRemoteUser());
+
+               // create display
+               final Display display = PlatformUI.createDisplay();
+
+               // log in
+               final ILoginContext loginContext = SecureRapActivator
+                               .createLoginContext(SecureRapActivator.CONTEXT_SPRING);
+               Subject subject = null;
+               tryLogin: while (subject == null && !display.isDisposed()) {
+                       try {
+                               loginContext.login();
+                               subject = loginContext.getSubject();
+
+                               // add security context to session
+                               if (httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY) == null)
+                                       httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY,
+                                                       SecurityContextHolder.getContext());
+                               // add thread locale to RWT session
+                               log.info("Locale "+LocaleUtils.threadLocale.get());
+                               RWT.setLocale(LocaleUtils.threadLocale.get());
+
+                               // Once the user is logged in, she can have a longer session
+                               // timeout
+                               RWT.getRequest().getSession()
+                                               .setMaxInactiveInterval(sessionTimeout);
+                               if (log.isDebugEnabled())
+                                       log.debug("Authenticated " + subject);
+                       } catch (LoginException e) {
+                               BadCredentialsException bce = wasCausedByBadCredentials(e);
+                               if (bce != null) {
+                                       MessageDialog.openInformation(display.getActiveShell(),
+                                                       "Bad Credentials", bce.getMessage());
+                                       // retry login
+                                       continue tryLogin;
+                               }
+                               return processLoginDeath(display, e);
+                       }
+               }
+
+               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 = createRapWorkbenchAdvisor(username);
+                                       int result = PlatformUI.createAndRunWorkbench(display,
+                                                       workbenchAdvisor);
+                                       return new Integer(result);
+                               }
+                       });
+                       // logout(loginContext, username);
+               } finally {
+                       display.dispose();
+               }
+               return returnCode;
+       }
+
+       private Integer processLoginDeath(Display display, LoginException 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 ArgeoException(
+                                       "Unexpected exception during authentication", e);
+               }
+
+       }
+
+       /** Recursively look for {@link BadCredentialsException} in the root causes. */
+       private BadCredentialsException wasCausedByBadCredentials(Throwable t) {
+               if (t instanceof BadCredentialsException)
+                       return (BadCredentialsException) t;
+
+               if (t.getCause() != null)
+                       return wasCausedByBadCredentials(t.getCause());
+               else
+                       return null;
+       }
+
+       /**
+        * 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.getCause() != null)
+                       return wasCausedByThreadDeath(t.getCause());
+               else
+                       return null;
+       }
+
+       protected void logout(ILoginContext secureContext, String username) {
+               try {
+                       HttpServletRequest httpRequest = RWT.getRequest();
+                       HttpSession httpSession = httpRequest.getSession();
+                       httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, null);
+                       RWT.getRequest().getSession().setMaxInactiveInterval(1);
+                       SecurityContextHolder.clearContext();
+                       secureContext.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/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/SecureRapActivator.java b/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/SecureRapActivator.java
new file mode 100644 (file)
index 0000000..4cbb441
--- /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.security.ui.rap;
+
+import java.net.URL;
+
+import org.eclipse.equinox.security.auth.ILoginContext;
+import org.eclipse.equinox.security.auth.LoginContextFactory;
+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";
+       public final static String CONTEXT_SPRING = "SPRING";
+       public final static String CONTEXT_SPRING_ANONYMOUS = "SPRING_ANONYMOUS";
+       private static final String JAAS_CONFIG_FILE = "/META-INF/jaas_default.txt";
+
+       private BundleContext bundleContext;
+       private static SecureRapActivator activator = null;
+
+       public void start(BundleContext bundleContext) throws Exception {
+               activator = this;
+               this.bundleContext = bundleContext;
+       }
+
+       public void stop(BundleContext context) throws Exception {
+               bundleContext = null;
+               activator = null;
+       }
+
+       public BundleContext getBundleContext() {
+               return bundleContext;
+       }
+
+       public static SecureRapActivator getActivator() {
+               return activator;
+       }
+
+       static ILoginContext createLoginContext(String contextName) {
+               URL configUrl = getActivator().getBundleContext().getBundle()
+                               .getEntry(JAAS_CONFIG_FILE);
+               return LoginContextFactory.createContext(contextName, configUrl);
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/commands/UserMenu.java b/trunk/security/plugins/org.argeo.security.ui.rap/src/main/java/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/trunk/security/plugins/org.argeo.security.ui.rcp/.classpath b/trunk/security/plugins/org.argeo.security.ui.rcp/.classpath
new file mode 100644 (file)
index 0000000..8cf7f48
--- /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.6"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/.project b/trunk/security/plugins/org.argeo.security.ui.rcp/.project
new file mode 100644 (file)
index 0000000..feeafcf
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.security.ui.rcp</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/trunk/security/plugins/org.argeo.security.ui.rcp/.settings/org.eclipse.jdt.core.prefs b/trunk/security/plugins/org.argeo.security.ui.rcp/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..1f382cb
--- /dev/null
@@ -0,0 +1,8 @@
+#Sat Jan 15 17:51:30 CET 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/META-INF/jaas_default.txt b/trunk/security/plugins/org.argeo.security.ui.rcp/META-INF/jaas_default.txt
new file mode 100644 (file)
index 0000000..16d476d
--- /dev/null
@@ -0,0 +1,27 @@
+OS_SPRING {
+    org.eclipse.equinox.security.auth.module.ExtensionLoginModule required
+        extensionId="org.argeo.security.equinox.osSpringLoginModule";
+};
+
+NIX {
+    org.eclipse.equinox.security.auth.module.ExtensionLoginModule requisite
+        extensionId="org.argeo.security.equinox.unixLoginModule";
+    org.eclipse.equinox.security.auth.module.ExtensionLoginModule required
+        extensionId="org.argeo.security.equinox.osSpringLoginModule";
+};
+
+WINDOWS {
+    org.eclipse.equinox.security.auth.module.ExtensionLoginModule requisite
+        extensionId="org.argeo.security.equinox.ntLoginModule";
+    org.eclipse.equinox.security.auth.module.ExtensionLoginModule required
+        extensionId="org.argeo.security.equinox.osSpringLoginModule";
+};
+
+REMOTE {
+    org.eclipse.equinox.security.auth.module.ExtensionLoginModule sufficient
+        extensionId="org.argeo.security.equinox.springLoginModuleRemote";
+};
+
+KEYRING {
+    org.argeo.security.crypto.KeyringLoginModule required;
+};
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/build.properties b/trunk/security/plugins/org.argeo.security.ui.rcp/build.properties
new file mode 100644 (file)
index 0000000..5ac3de9
--- /dev/null
@@ -0,0 +1,6 @@
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = plugin.xml,\
+               META-INF/,\
+               .,\
+               log4j.properties
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_about.gif b/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_about.gif
new file mode 100644 (file)
index 0000000..20d9ad2
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_about.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_launcher.icns b/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_launcher.icns
new file mode 100644 (file)
index 0000000..b77a6a6
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_launcher.icns differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_launcher.ico b/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_launcher.ico
new file mode 100644 (file)
index 0000000..d548f71
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_launcher.ico differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_launcher.xpm b/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_launcher.xpm
new file mode 100644 (file)
index 0000000..b0c139e
--- /dev/null
@@ -0,0 +1,307 @@
+/* XPM */\r
+static char * icon48_xpm[] = {\r
+"48 48 256 2",\r
+"      c #4B4B3B3B9090",\r
+".     c #0D0D0E0E5454",\r
+"X     c #11110E0E5B5B",\r
+"o     c #17170F0F6363",\r
+"O     c #1D1D13136969",\r
+"+     c #212114146C6C",\r
+"@     c #252514147171",\r
+"#     c #282811116C6C",\r
+"$     c #26260D0D6363",\r
+"%     c #22220B0B5E5E",\r
+"&     c #1C1C0B0B5A5A",\r
+"*     c #1C1C0B0B5252",\r
+"=     c #1B1B05055353",\r
+"-     c #161606064D4D",\r
+";     c #161605054949",\r
+":     c #111104044848",\r
+">     c #131304044545",\r
+",     c #131305054242",\r
+"<     c #141410105E5E",\r
+"1     c #2C2C15157373",\r
+"2     c #2B2B1B1B7575",\r
+"3     c #343416167272",\r
+"4     c #313113136E6E",\r
+"5     c #222209095757",\r
+"6     c #1B1B06064D4D",\r
+"7     c #15150B0B4242",\r
+"8     c #13130C0C5555",\r
+"9     c #2E2E1B1B7878",\r
+"0     c #33331F1F7C7C",\r
+"q     c #343418187878",\r
+"w     c #3B3B1C1C7575",\r
+"e     c #2E2E10106767",\r
+"r     c #1B1B07074747",\r
+"t     c #18180B0B4646",\r
+"y     c #151513136262",\r
+"u     c #1A1A15156464",\r
+"i     c #34341F1F7777",\r
+"p     c #40401E1E8080",\r
+"a     c #42421B1B7A7A",\r
+"s     c #3B3B15157474",\r
+"d     c #2B2B0B0B5B5B",\r
+"f     c #222207075252",\r
+"g     c #373727277A7A",\r
+"h     c #474724248484",\r
+"j     c #393915156E6E",\r
+"k     c #373711116A6A",\r
+"l     c #343413136363",\r
+"z     c #232319196E6E",\r
+"x     c #292919197070",\r
+"c     c #3C3C2C2C8282",\r
+"v     c #444431318585",\r
+"b     c #494934348A8A",\r
+"n     c #505026268A8A",\r
+"m     c #3D3D1B1B6E6E",\r
+"M     c #31310E0E5C5C",\r
+"N     c #2B2B0D0D5353",\r
+"B     c #222207074A4A",\r
+"V     c #52523C3C9292",\r
+"C     c #58583C3C9494",\r
+"Z     c #5D5D44449797",\r
+"A     c #5C5C2E2E9292",\r
+"S     c #676733339595",\r
+"D     c #424228287575",\r
+"F     c #29290A0A4F4F",\r
+"G     c #6C6C4A4A9E9E",\r
+"H     c #72725454A7A7",\r
+"J     c #8C8C6D6DB2B2",\r
+"K     c #343424246E6E",\r
+"L     c #3A3A23236A6A",\r
+"P     c #3A3A1C1C6767",\r
+"I     c #24240A0A4B4B",\r
+"U     c #151518186161",\r
+"Y     c #76766F6FA5A5",\r
+"T     c #ADAD9191CCCC",\r
+"R     c #98988989D3D3",\r
+"E     c #45453B3B8686",\r
+"W     c #3C3C35357979",\r
+"Q     c #363631317575",\r
+"!     c #32322D2D6B6B",\r
+"~     c #323229296363",\r
+"^     c #30301F1F6262",\r
+"/     c #323218185E5E",\r
+"(     c #272707074B4B",\r
+")     c #202028286C6C",\r
+"_     c #1E1E1D1D6868",\r
+"`     c #9A9A8282BBBB",\r
+"'     c #C8C8B3B3D3D3",\r
+"]     c #B3B3AFAFE7E7",\r
+"[     c #84847272C6C6",\r
+"{     c #58585757A3A3",\r
+"}     c #3F3F3C3C8A8A",\r
+"|     c #3B3B3A3A8484",\r
+" .    c #414139397D7D",\r
+"..    c #3D3D39397A7A",\r
+"X.    c #37372E2E6E6E",\r
+"o.    c #2C2C21215A5A",\r
+"O.    c #2E2E1B1B5B5B",\r
+"+.    c #F5F5EFEFF5F5",\r
+"@.    c #656566669A9A",\r
+"#.    c #47474B4B8E8E",\r
+"$.    c #3C3C44447B7B",\r
+"%.    c #444442428080",\r
+"&.    c #45453E3E8181",\r
+"*.    c #40403C3C8181",\r
+"=.    c #3D3D33337474",\r
+"-.    c #3B3B30306E6E",\r
+";.    c #38382D2D6969",\r
+":.    c #303026265D5D",\r
+">.    c #2C2C15155A5A",\r
+",.    c #1F1F1C1C7070",\r
+"<.    c #25251E1E7171",\r
+"1.    c #59595C5C9191",\r
+"2.    c #4D4D53538989",\r
+"3.    c #4C4C49498484",\r
+"4.    c #484845458585",\r
+"5.    c #494941418585",\r
+"6.    c #494940408181",\r
+"7.    c #2F2F1C1C5353",\r
+"8.    c #2B2B28287676",\r
+"9.    c #323231317F7F",\r
+"0.    c #545452528B8B",\r
+"q.    c #51514E4E8989",\r
+"w.    c #4E4E4B4B8C8C",\r
+"e.    c #4C4C47478686",\r
+"r.    c #46463D3D7E7E",\r
+"t.    c #434336367A7A",\r
+"y.    c #2B2B13135555",\r
+"u.    c #47473D3D8D8D",\r
+"i.    c #575757578E8E",\r
+"p.    c #48483E3E7F7F",\r
+"a.    c #46463A3A7D7D",\r
+"s.    c #424235357575",\r
+"d.    c #404034347171",\r
+"f.    c #BFBFCBCBFAFA",\r
+"g.    c #B8B8A8A8DDDD",\r
+"h.    c #5E5E60609292",\r
+"j.    c #565655558C8C",\r
+"k.    c #4B4B44448282",\r
+"l.    c #454539397B7B",\r
+"z.    c #434338387878",\r
+"x.    c #3F3F32326D6D",\r
+"c.    c #3D3D30306969",\r
+"v.    c #3A3A2E2E6363",\r
+"b.    c #36362A2A5C5C",\r
+"n.    c #343424245555",\r
+"m.    c #30301E1E4D4D",\r
+"M.    c #49493C3C8282",\r
+"N.    c #5E5E4F4F8C8C",\r
+"B.    c #56563B3B8B8B",\r
+"V.    c #545407078585",\r
+"C.    c #424234347272",\r
+"Z.    c #9797A4A4F7F7",\r
+"A.    c #444436367676",\r
+"S.    c #7D7D7979D5D5",\r
+"D.    c #464640408A8A",\r
+"F.    c #44444B4B8282",\r
+"G.    c #414107077777",\r
+"H.    c #71716161C1C1",\r
+"J.    c #303039397979",\r
+"K.    c #8E8E8E8EE6E6",\r
+"L.    c #404033338B8B",\r
+"P.    c #4A4A45458C8C",\r
+"I.    c #46463A3A8080",\r
+"U.    c #363629295454",\r
+"Y.    c #303022224848",\r
+"T.    c #424237377575",\r
+"R.    c #2E2E1D1D6363",\r
+"E.    c #79798383EAEA",\r
+"W.    c #74747B7BE4E4",\r
+"Q.    c #6D6D7676D6D6",\r
+"!.    c #6A6A7171CECE",\r
+"~.    c #66666969C6C6",\r
+"^.    c #62626565BCBC",\r
+"/.    c #5F5F6060B5B5",\r
+"(.    c #5B5B5B5BACAC",\r
+").    c #535353539898",\r
+"_.    c #4F4F4F4FA4A4",\r
+"`.    c #54544D4DA4A4",\r
+"'.    c #323204046B6B",\r
+"].    c #303035357979",\r
+"[.    c #313122224343",\r
+"{.    c #5A5A5B5BB7B7",\r
+"}.    c #484846468080",\r
+"|.    c #454541417575",\r
+" X    c #4B4B31318282",\r
+".X    c #47473C3C8484",\r
+"XX    c #3E3E35356E6E",\r
+"oX    c #2F2F26264040",\r
+"OX    c #2B2B23233A3A",\r
+"+X    c #262619195C5C",\r
+"@X    c #252515155A5A",\r
+"#X    c #55555151B3B3",\r
+"$X    c #3C3C2D2D5D5D",\r
+"%X    c #39392F2F5656",\r
+"&X    c #37372D2D5050",\r
+"*X    c #25251F1F3030",\r
+"=X    c #24241D1D4343",\r
+"-X    c #202013135656",\r
+";X    c #41413B3B6C6C",\r
+":X    c #444442429696",\r
+">X    c #212100005E5E",\r
+",X    c #444436367272",\r
+"<X    c #444436366F6F",\r
+"1X    c #424236366868",\r
+"2X    c #3F3F34346161",\r
+"3X    c #3C3C32325A5A",\r
+"4X    c #34342A2A4A4A",\r
+"5X    c #21211B1B2121",\r
+"6X    c #22221C1C6363",\r
+"7X    c #2D2D01015E5E",\r
+"8X    c #20201B1B2525",\r
+"9X    c #49492F2F7B7B",\r
+"0X    c #434334346C6C",\r
+"qX    c #323229294545",\r
+"wX    c #1C1C10104F4F",\r
+"eX    c #222217176363",\r
+"rX    c #37371E1E6B6B",\r
+"tX    c #424232326E6E",\r
+"yX    c #444433336969",\r
+"uX    c #424233336565",\r
+"iX    c #1E1E1A1A1E1E",\r
+"pX    c #161601014C4C",\r
+"aX    c #1C1C11115454",\r
+"sX    c #252503035B5B",\r
+"dX    c #414131316A6A",\r
+"fX    c #22221F1F7878",\r
+"gX    c #272724246767",\r
+"hX    c #1D1D16165E5E",\r
+"jX    c #131305055151",\r
+"kX    c #40402F2F6464",\r
+"lX    c #111107074E4E",\r
+"zX    c #0C0C05054B4B",\r
+"xX    c #22221E1E5757",\r
+"cX    c #1A1A0D0D4D4D",\r
+"vX    c #16160B0B4D4D",\r
+"bX    c #060604044747",\r
+"nX    c #040403034343",\r
+"mX    c #3B3B27276565",\r
+"MX    c #0E0E03034141",\r
+"NX    c #111106064A4A",\r
+"BX    c #0E0E04044646",\r
+"VX    c #111105053B3B",\r
+"CX    c #0D0D01013A3A",\r
+"ZX    c #030305053F3F",\r
+"AX    c #0F0F0F0F4C4C",\r
+"SX    c #020207073B3B",\r
+"DX    c #0D0D03034646",\r
+"FX    c #0B0B02023F3F",\r
+"GX    c #0D0D02023434",\r
+"HX    c #0A0A0E0E4444",\r
+"JX    c #161614145252",\r
+"KX    c #0B0B04044646",\r
+"LX    c #0A0A03034444",\r
+"PX    c #1D1D21215757",\r
+"IX    c #090910104040",\r
+"UX    c #18181E1E5353",\r
+"                                                                                                ",\r
+"  . . . . X X o o o o O + + @ @ @ @ @ @ @ # # # # # # $ $ % % & & & & * = - - - ; : > > , , ,   ",\r
+"  . . X X < < o o O O + @ 1 2 2 1 1 1 1 1 1 1 1 3 4 # # $ $ $ % % % 5 = = 6 6 - ; ; > > , , 7   ",\r
+"  . 8 < < < o O O + @ @ 2 9 9 0 0 q q q q q q q w 3 4 4 e e $ $ $ $ % 5 = = 6 6 6 ; ; > > r t   ",\r
+"  . X < y u O O + @ 1 2 i 0 0 p p p p p p p a a p a s 3 4 4 e e e e d 5 5 f f 6 6 6 ; ; r r t   ",\r
+"  X < y u O + + @ 2 9 0 g p p h h h h h h h p h h a a s s j k k j l d d 5 5 f f 6 6 r r r r r   ",\r
+"  X < y O z x x 9 0 g c v b n n n n n n n n n n n h a a s s j j m l M d d N f f B B B B r r r   ",\r
+"  < y u + z 2 0 c c b V C Z C C A A A A A S S S n h D w w m m m m k l M d d N f F F F B r r r   ",\r
+"  < y O z z 9 c b V Z G H H H G S G G J J H V v c g g K K K K L P l l l M M N N N N I B B r r   ",\r
+"  U u + z x i v C H Y J J J J H J T R H V E W W Q Q Q Q ! ! ! ~ ^ ^ / / M M M M M N ( B B r r   ",\r
+"  ) _ z x 9 g b Z Y ` ' ' ' T T ] [ { } |  .........W W Q Q X.! ~ ~ o.O./ / / / N F ( B B B r   ",\r
+"  ) ) z x i c V G J ' +.+.+.+.] [ @.#.$.%.%.%.&.*. . .....W =.-.X.;.~ :.o.O.^ >.N F ( B B B B   ",\r
+"  ) ,.<.2 g v C H J ' +.+.+.+.T Y 1.2.3.4.4.4.5.6.&.&. . ...W =.=.-.;.~ :.o.7.>.N F F ( B I I   ",\r
+"  ) ,.<.8.9.b C H J T +.+.+.' J @.1.0.q.q.w.e.4.5.6.&.r. . .t.W =.=.-.;.~ :.o.7.y.N F ( ( I I   ",\r
+"  8.8.8.9.| u.C G J T +.+.' ` Y @.1.i.0.q.q.e.e.5.6.p.r.a. .t.t.s.d.d.-.;.~ :.7.7.y.F F ( I I   ",\r
+"  9.c c c v b A S H ' f.g.` Y @.h.1.i.j.0.q.q.e.k.6.p.r.a.l.t.z.s.s.d.x.c.v.b.n.m.7.N F F F I   ",\r
+"  <.g M.N.B.B.n V.J f.R J @.h.h.1.i.i.j.j.0.q.3.k.6.p.r.a.l.l.z.s.C.d.x.x.c.v.b.n.m.y.N F F F   ",\r
+"  + x i v B.Z Z G g.Z.[ @.2.i.i.j.j.j.j.j.0.q.3.k.6.p.p.a.l.l.A.s.C.C.d.x.c.v.b.n.m.7.y.N N y.  ",\r
+"  O # 3 w p n Z ` f.S.H D.F.q.0.0.0.0.j.0.q.q.3.k.6.p.r.a.a.l.A.s.s.C.d.x.x.c.v.b.n.m.y.y.y.y.  ",\r
+"  O # 4 s a G.V.T Z.H.V J.%.e.w.q.0.0.0.0.q.q.e.k.6.p.r.a.a.l.z.A.s.C.d.d.x.c.v.b.n.m.7.>.y.y.  ",\r
+"  O # 4 s w G.A f.K.H L...4.P.w.w.w.q.q.q.q.e.e.5.6.M.I.I.a.a.l.z.A.s.d.d.x.c.v.v.U.Y.7.O.>.y.  ",\r
+"  $ # 4 3 s G.G f.S.Z 9.*.D.P.P.w.w.w.w.w.e.e.5.5.M.M.I.I.a.a.l.z.T.s.d.d.x.x.c.v.U.Y.m.R.>.>.  ",\r
+"  E.E.W.W.W.W.E.Z.E.W.Q.Q.Q.Q.Q.Q.Q.Q.Q.Q.!.!.!.!.!.~.~.~.~.~.~.^.^.^.^.^./././.(.{ { )._.`._.  ",\r
+"  + # 3 w s '.` f.H.V ].*.D.P.P.P.P.P.P.P.5.5.5.M.M.M.I.I.a.l.z.z.T.T.s.d.d.x.c.v.U.Y.[.^ O.>.  ",\r
+"  ~.~.~.~.~.~.K.Z.S.~.{.^.^.^.^.^.^.^.^././././././.(.(.(.(.(.(.{ { { { { ).).).w.w.}.|.P.u.}   ",\r
+"   .I.6. X X XT f.H.b ].*.E D.D.D.D.5.5..X.XM.M.I.I.a.a.l.l.z.z.T.T.T.d.XXx.c.v.b.U.oXOXR.+X@X  ",\r
+"  E.E.E.W.W.W.K.Z.E.W.Q.Q.Q.Q.Q.Q.Q.Q.!.!.!.!.!.!.~.~.~.~.~.~.^.^.^.^.^./././.(.{ { ).).#X_._.  ",\r
+"  & % $ e '.= H f.H.b 8.W *.E E E .X.X.XI.I.I.I.a.a.l.l.z.z.T.T.T.C.XXXXc.v.$X%X&XY.*X=X2 @X-X  ",\r
+"  {.{.{.{.{.#X~.Z.W.~.{.{./././././././././.(.(.(.(.(.(.(.{ { { { { { ).).).w.w.3.}.;X%.:X} }   ",\r
+"  & & % $ e >XV.f.S.C K X.W t. . .l.l.t.t.t.t.A.A.A.,X,X,X,X,X<X;X1X1X2X3X%X&X4XoX*X5Xo.6X-X-X  ",\r
+"  & & % $ $ >X7X` Z.Z D ! =.=.s.t.t.t.A.A.s.A.,X,X,X,X<X<X<X<X;X1X2X2X3X%X&X4XoXOX8X5X<.@X-X-X  ",\r
+"  8 & & $ e K 9XJ f.^. XK X.=.=.s.s.s.s.s.C.C.C.C.<X0X0X0X1X1X1X2X2X3X%X&X4XqXOX*X8X=X<.-X-XwX  ",\r
+"  8 & eXK k.@.M. Xg.E.B.rX~ -.d.d.C.C.C.C.C.tX0X0X0XyXyX1X1XuX2X2X3X%X&X4XqXoXOX8XiX6XeX-X-XwX  ",\r
+"  6XQ h.Y N.K e pXB.f./.9Xo.;.-.x.d.tXtXtXtXtX0X0XyXyXyXuXuX2X2X3X%X&X4XqXoXOX*XiX=X_ aX-XaXwX  ",\r
+"  Y Y  .R.@X% sX= 7XT Z.V P :.;.c.x.x.x.tXdXdXdXyXyXuXuX2X2X3X3X%X&X4XqXoXOX*X8X*XfXaXaXaXwXwX  ",\r
+"  gXhX& * & 5 5 sXjXa ] S.9X/ ~ v.c.dXdXdXdXdXuXuXuXkX2X2X3X%X%X&X4XqXoXOX*X8XiX<.hXwXwXaXwXwX  ",\r
+"  8 lXlXjX= * 5 5 = zX9X] H.D 7.b.v.v.kXc.kXkXkXkXkX2X$X3X%X&X&X4XqXoXOX*X8XiXxXhXwXcXcXwXwXwX  ",\r
+"  zXzXlXlXjX= = f f sXD Y ] ~.D 7.n.b.v.v.kXkXkXkX$X$X%X%X&X4XqXoXoXOX*X8X5XgXu cXvXvXcXcXwXwX  ",\r
+"  bXbXzXlXlX- = * @XX.h.e D T S.D n.m.b.$X$X$X$X$X%X%X&X&X4XqXoXOX*X8X8X*X<.hXvX; ; vXvXvXcXwX  ",\r
+"  nXbXzXzXlXlX- -XX.Y R.pXpXP J K.Z mXm.n.U.U.U.U.U.4X4XY.oXOX*X8X8X8X=X,.aXvX; > > > t t t cX  ",\r
+"  nXbXbXzXzXlXaXXXY +Xf = f = 7XtXR R   mXn.Y.[.[.[.[.OX*X*X8X5X*X=X,.u vXvX; > > MXMX, 7 t t   ",\r
+"  nXnXnXbXzXvX-.Y +XcX6 6 = = = d L B.G H `.X.:.7.m.=X=X=X=X=X6X,.u cXvXNX: BX> MXVXCXVX7 7 7   ",\r
+"  ZXZXnXbXAXgX@.+XvX; - - - - * @XR.>.sX>.R.i 0 x eXeXeXeXO hXaXcXvXNX: BXMXMXMXVXCXCXCXVX7 7   ",\r
+"  SXZXZXAXgXN.aXNX: : NXNXNX- * +X+X* 6 6 6 * * * * * * * vXvX- NX: DXBXMXMXFXCXCXCXCXGXVXVX7   ",\r
+"  SXSXHXgX0.JXKXLXKXKXDX: : NX* @X-X- - - - - - - - - - - NX: DXDXDXMXMXFXFXCXCXCXCXGXGXGXVXVX  ",\r
+"  SXHXPX3.JXnXnXnXnXbXLXKXKXNXaX-XvX: : NX: : : : : : : BXDXDXLXMXMXFXFXCXCXCXCXGXGXGXGXGXGXGX  ",\r
+"  IXUX$.AXZXZXZXZXnXnXnXLXLXNXJXvXBXDXBXBXBXBXDXDXDXDXLXDXLXLXFXFXFXFXCXCXCXCXGXGXGXGXGXGXGXGX  ",\r
+"                                                                                                "};\r
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_window_16.gif b/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_window_16.gif
new file mode 100644 (file)
index 0000000..05626b1
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_window_16.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_window_32.gif b/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_window_32.gif
new file mode 100644 (file)
index 0000000..b432f88
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui.rcp/icons/alt_window_32.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/plugin.xml b/trunk/security/plugins/org.argeo.security.ui.rcp/plugin.xml
new file mode 100644 (file)
index 0000000..4ddf700
--- /dev/null
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+
+       <!-- We use the product to define some of the product preferences 
+       see http://dev.eclipse.org/viewcvs/viewvc.cgi/platform-ui-home/rcp/faq.html?revision=1.6#customPrefs --> 
+       <extension
+         id="org.argeo.security.ui.product"
+         point="org.eclipse.core.runtime.products">
+      <product
+            name="ArgeoProduct" application="org.argeo.security.ui.rcp.secureUi">
+          <property
+               name="appName"
+               value="Secure UI">
+         </property>
+            <property
+               name="preferenceCustomization"
+               value="plugin_customization.ini"/>
+      </product>
+   </extension>  
+
+   <extension
+         id="secureUi"
+         name="Argeo Secure UI"
+         point="org.eclipse.core.runtime.applications">
+      <application cardinality="singleton-global"
+         thread="main"
+         visible="true">
+         <run
+               class="org.argeo.security.ui.rcp.SecureRcp">
+         </run>
+      </application>
+   </extension>
+   
+   <extension
+         point="org.eclipse.equinox.security.callbackHandlerMapping">
+      <callbackHandlerMapping
+            callbackHandlerId="org.argeo.security.ui.defaultLoginDialog"
+            configName="REMOTE">
+      </callbackHandlerMapping>
+   </extension>
+   
+       <!-- COMMANDS --> 
+       <extension point="org.eclipse.ui.commands">
+               <!-- Register a default command that enable an "open file" action in a single sourced application  -->  
+               <command
+                       defaultHandler="org.argeo.eclipse.ui.specific.OpenFile"
+                       id="org.argeo.security.ui.specific.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>
+</plugin>
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/plugin_customization.ini b/trunk/security/plugins/org.argeo.security.ui.rcp/plugin_customization.ini
new file mode 100644 (file)
index 0000000..baf9cef
--- /dev/null
@@ -0,0 +1,5 @@
+org.eclipse.ui/DOCK_PERSPECTIVE_BAR=topRight
+org.eclipse.ui/SHOW_TEXT_ON_PERSPECTIVE_BAR=true
+org.eclipse.ui/PERSPECTIVE_BAR_SIZE=100
+org.eclipse.ui/SHOW_TRADITIONAL_STYLE_TABS=true
+org.eclipse.ui/SHOW_PROGRESS_ON_STARTUP = false
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/pom.xml b/trunk/security/plugins/org.argeo.security.ui.rcp/pom.xml
new file mode 100644 (file)
index 0000000..ee8371e
--- /dev/null
@@ -0,0 +1,83 @@
+<?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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>plugins</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.ui.rcp</artifactId>
+       <name>Commons Security UI RCP</name>
+       <packaging>jar</packaging>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-Activator>org.argeo.security.ui.rcp.SecureApplicationActivator</Bundle-Activator>
+                                               <Bundle-ActivationPolicy>lazy</Bundle-ActivationPolicy>
+                                               <Require-Bundle>org.eclipse.ui,org.eclipse.core.runtime</Require-Bundle>
+                                               <Import-Package>
+                                                       org.argeo.eclipse.ui.specific,
+                                                       org.argeo.eclipse.spring,
+                                                       *
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- Commons -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+
+               <!-- Argeo Security -->
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.ui</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.equinox</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- RCP specific -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui.rcp</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.dep.rcp</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/AbstractSecureApplication.java b/trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/AbstractSecureApplication.java
new file mode 100644 (file)
index 0000000..75184cb
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * 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.rcp;
+
+import java.security.PrivilegedAction;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.OperatingSystem;
+import org.eclipse.equinox.app.IApplication;
+import org.eclipse.equinox.app.IApplicationContext;
+import org.eclipse.equinox.security.auth.ILoginContext;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.application.WorkbenchAdvisor;
+
+/**
+ * RCP workbench initialization
+ */
+public abstract class AbstractSecureApplication implements IApplication {
+       final static String NODE_REPO_URI = "argeo.node.repo.uri";
+
+       private static final Log log = LogFactory
+                       .getLog(AbstractSecureApplication.class);
+
+       protected WorkbenchAdvisor createWorkbenchAdvisor(String username) {
+               return new SecureWorkbenchAdvisor(username);
+       }
+
+       public Object start(IApplicationContext context) throws Exception {
+               // wait for the system to be initialized
+               // try {
+               // Thread.sleep(3000);
+               // } catch (Exception e2) {
+               // // silent
+               // }
+
+               boolean remote = System.getProperty(NODE_REPO_URI) != null;
+
+               // choose login context
+               final ILoginContext loginContext;
+               if (remote) {
+                       loginContext = SecureApplicationActivator
+                                       .createLoginContext(SecureApplicationActivator.CONTEXT_REMOTE);
+               } else {
+                       if (OperatingSystem.os == OperatingSystem.WINDOWS)
+                               loginContext = SecureApplicationActivator
+                                               .createLoginContext(SecureApplicationActivator.CONTEXT_WINDOWS);
+                       else
+                               loginContext = SecureApplicationActivator
+                                               .createLoginContext(SecureApplicationActivator.CONTEXT_NIX);
+               }
+
+               final Display display = PlatformUI.createDisplay();
+
+               // login
+               Subject subject = null;
+               try {
+                       loginContext.login();
+                       subject = loginContext.getSubject();
+               } catch (LoginException e) {
+                       log.error("Error when logging in.", e);
+                       display.dispose();
+                       try {
+                               Thread.sleep(2000);
+                       } catch (InterruptedException e1) {
+                               // silent
+                       }
+                       return null;
+               }
+
+               // identify after successful login
+               if (log.isDebugEnabled())
+                       log.debug("subject=" + subject);
+               final String username = subject.getPrincipals().iterator().next()
+                               .getName();
+               if (log.isDebugEnabled())
+                       log.debug(username + " logged in");
+//             display.disposeExec(new Runnable() {
+//                     public void run() {
+//                             log.debug("Display disposed");
+//                             logout(loginContext, username);
+//                     }
+//             });
+
+               try {
+                       PrivilegedAction<?> privilegedAction = new PrivilegedAction<Object>() {
+                               public Object run() {
+                                       int result = PlatformUI.createAndRunWorkbench(display,
+                                                       createWorkbenchAdvisor(username));
+                                       return new Integer(result);
+                               }
+                       };
+
+                       Integer returnCode = (Integer) Subject.doAs(subject,
+                                       privilegedAction);
+                       logout(loginContext, username);
+                       return processReturnCode(returnCode);
+               } catch (Exception e) {
+                       if (subject != null)
+                               logout(loginContext, username);
+                       log.error("Unexpected error", e);
+               } finally {
+                       display.dispose();
+               }
+               return null;
+       }
+
+       protected Integer processReturnCode(Integer returnCode) {
+               if (returnCode == PlatformUI.RETURN_RESTART)
+                       return IApplication.EXIT_RESTART;
+               else
+                       return IApplication.EXIT_OK;
+       }
+
+       static void logout(ILoginContext secureContext, String username) {
+               try {
+                       secureContext.logout();
+                       log.info("Logged out " + (username != null ? username : "")
+                                       + " (THREAD=" + Thread.currentThread().getId() + ")");
+               } catch (LoginException e) {
+                       log.error("Erorr when logging out", e);
+               }
+       }
+
+       public void stop() {
+               final IWorkbench workbench;
+               try {
+                       workbench = PlatformUI.getWorkbench();
+               } catch (Exception e) {
+                       return;
+               }
+               if (workbench == null)
+                       return;
+               final Display display = workbench.getDisplay();
+               if (display != null && !display.isDisposed())
+                       display.syncExec(new Runnable() {
+
+                               public void run() {
+                                       if (!display.isDisposed())
+                                               workbench.close();
+                               }
+                       });
+
+               if (log.isDebugEnabled())
+                       log.debug("workbench stopped");
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureActionBarAdvisor.java b/trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureActionBarAdvisor.java
new file mode 100644 (file)
index 0000000..481865b
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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.rcp;
+
+import org.eclipse.jface.action.GroupMarker;
+import org.eclipse.jface.action.ICoolBarManager;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.ui.IWorkbenchActionConstants;
+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;
+
+public class SecureActionBarAdvisor extends ActionBarAdvisor {
+       private IWorkbenchAction exitAction;
+       private IWorkbenchAction openPerspectiveDialogAction;
+       private IWorkbenchAction showViewMenuAction;
+       private IWorkbenchAction preferences;
+       private IWorkbenchAction saveAction;
+       private IWorkbenchAction saveAsAction;
+       private IWorkbenchAction saveAllAction;
+       private IWorkbenchAction closeAllAction;
+
+       // private final Boolean isRcp;
+
+       public SecureActionBarAdvisor(IActionBarConfigurer configurer, Boolean isRcp) {
+               super(configurer);
+               // this.isRcp = isRcp;
+       }
+
+       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);
+
+               exitAction = ActionFactory.QUIT.create(window);
+               register(exitAction);
+
+               // Save semantiocs
+               saveAction = ActionFactory.SAVE.create(window);
+               register(saveAction);
+               saveAsAction = ActionFactory.SAVE_AS.create(window);
+               register(saveAsAction);
+               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(saveAsAction);
+               fileMenu.add(saveAllAction);
+               fileMenu.add(closeAllAction);
+               fileMenu.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
+               fileMenu.add(new Separator());
+               fileMenu.add(exitAction);
+
+               // Edit
+               editMenu.add(preferences);
+
+               // Window
+               windowMenu.add(openPerspectiveDialogAction);
+               windowMenu.add(showViewMenuAction);
+       }
+
+       @Override
+       protected void fillCoolBar(ICoolBarManager coolBar) {
+               IToolBarManager saveToolbar = new ToolBarManager(SWT.FLAT | SWT.RIGHT);
+               saveToolbar.add(saveAction);
+               saveToolbar.add(saveAllAction);
+               coolBar.add(saveToolbar);
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureApplicationActivator.java b/trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureApplicationActivator.java
new file mode 100644 (file)
index 0000000..9c0425c
--- /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.rcp;
+
+import java.net.URL;
+
+import org.eclipse.equinox.security.auth.ILoginContext;
+import org.eclipse.equinox.security.auth.LoginContextFactory;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/** Activator able to create {@link ILoginContext} */
+public class SecureApplicationActivator implements BundleActivator {
+
+       public final static String CONTEXT_REMOTE = "REMOTE";
+       public final static String CONTEXT_NIX = "NIX";
+       public final static String CONTEXT_WINDOWS = "WINDOWS";
+       private static final String JAAS_CONFIG_FILE = "/META-INF/jaas_default.txt";
+
+       private static BundleContext bundleContext;
+
+       public void start(BundleContext bundleContext) throws Exception {
+               SecureApplicationActivator.bundleContext = bundleContext;
+       }
+
+       public void stop(BundleContext context) throws Exception {
+       }
+
+       static ILoginContext createLoginContext(String context) {
+               URL configUrl = bundleContext.getBundle().getEntry(JAAS_CONFIG_FILE);
+               return LoginContextFactory.createContext(context, configUrl);
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureRcp.java b/trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureRcp.java
new file mode 100644 (file)
index 0000000..d8125cf
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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.rcp;
+
+
+public class SecureRcp extends AbstractSecureApplication {
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureWorkbenchAdvisor.java b/trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureWorkbenchAdvisor.java
new file mode 100644 (file)
index 0000000..33b781e
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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.rcp;
+
+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;
+
+/**
+ * Workbench configuration which is aware of the logged in user and can remember
+ * workbench state.
+ */
+public class SecureWorkbenchAdvisor 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 final String username;
+
+       public SecureWorkbenchAdvisor(String username) {
+               this.username = username;
+       }
+
+       @Override
+       public void initialize(final IWorkbenchConfigurer configurer) {
+               super.initialize(configurer);
+               Boolean saveAndRestore = Boolean.parseBoolean(System.getProperty(
+                               SAVE_AND_RESTORE_PROPERTY, "true"));
+               configurer.setSaveAndRestore(saveAndRestore);
+       }
+
+       public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(
+                       IWorkbenchWindowConfigurer configurer) {
+               return new SecureWorkbenchWindowAdvisor(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;
+       }
+
+       protected String getUsername() {
+               return username;
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureWorkbenchWindowAdvisor.java b/trunk/security/plugins/org.argeo.security.ui.rcp/src/main/java/org/argeo/security/ui/rcp/SecureWorkbenchWindowAdvisor.java
new file mode 100644 (file)
index 0000000..d8aaec4
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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.rcp;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.application.ActionBarAdvisor;
+import org.eclipse.ui.application.IActionBarConfigurer;
+import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
+import org.eclipse.ui.application.WorkbenchWindowAdvisor;
+
+public class SecureWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor {
+       private final String username;
+
+       public SecureWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer,
+                       String username) {
+               super(configurer);
+               this.username = username;
+       }
+
+       public ActionBarAdvisor createActionBarAdvisor(
+                       IActionBarConfigurer configurer) {
+               return new SecureActionBarAdvisor(configurer, true);
+       }
+
+       public void preWindowOpen() {
+               IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
+               configurer.setInitialSize(new Point(1200, 900));
+               configurer.setShowCoolBar(true);
+               configurer.setShowMenuBar(true);
+               configurer.setShowStatusLine(true);
+               configurer.setShowProgressIndicator(true);
+
+               configurer.setShowPerspectiveBar(true);
+               String remoteUri = System
+                               .getProperty(AbstractSecureApplication.NODE_REPO_URI);
+               if (remoteUri != null)
+                       configurer
+                                       .setTitle("Argeo UI - " + username + " (" + remoteUri + ")"); //$NON-NLS-1$
+               else
+                       configurer.setTitle("Argeo UI - " + username); //$NON-NLS-1$
+
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui/.classpath b/trunk/security/plugins/org.argeo.security.ui/.classpath
new file mode 100644 (file)
index 0000000..5641c7c
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/security/plugins/org.argeo.security.ui/.project b/trunk/security/plugins/org.argeo.security.ui/.project
new file mode 100644 (file)
index 0000000..a052e9a
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.security.ui</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/trunk/security/plugins/org.argeo.security.ui/.settings/org.eclipse.pde.core.prefs b/trunk/security/plugins/org.argeo.security.ui/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..dfa507d
--- /dev/null
@@ -0,0 +1,3 @@
+#Sat Jan 15 18:15:48 CET 2011
+eclipse.preferences.version=1
+resolve.requirebundle=false
diff --git a/trunk/security/plugins/org.argeo.security.ui/META-INF/spring/commands.xml b/trunk/security/plugins/org.argeo.security.ui/META-INF/spring/commands.xml
new file mode 100644 (file)
index 0000000..3b2e6cc
--- /dev/null
@@ -0,0 +1,7 @@
+<?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">
+
+</beans>
diff --git a/trunk/security/plugins/org.argeo.security.ui/META-INF/spring/keyring.xml b/trunk/security/plugins/org.argeo.security.ui/META-INF/spring/keyring.xml
new file mode 100644 (file)
index 0000000..906c0b7
--- /dev/null
@@ -0,0 +1,26 @@
+<?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:util="http://www.springframework.org/schema/util"
+       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
+        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
+
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:securityui.properties</value>
+               </property>
+       </bean>
+
+       <bean id="nodeSession" class="org.argeo.jcr.spring.ThreadBoundSession">
+               <property name="repository" ref="nodeRepository" />
+       </bean>
+
+       <bean id="keyring" class="org.argeo.security.jcr.JcrKeyring">
+               <property name="session" ref="nodeSession" />
+               <property name="defaultCallbackHandler" ref="defaultCallbackHandler" />
+               <property name="secreteKeyLength" value="${argeo.keyring.secreteKeyLength}" />
+       </bean>
+
+</beans>
diff --git a/trunk/security/plugins/org.argeo.security.ui/META-INF/spring/osgi.xml b/trunk/security/plugins/org.argeo.security.ui/META-INF/spring/osgi.xml
new file mode 100644 (file)
index 0000000..01e2124
--- /dev/null
@@ -0,0 +1,22 @@
+<?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
+\r
+       <!-- REFERENCES -->\r
+       <reference id="secureLogger" interface="org.argeo.ArgeoLogger"\r
+               cardinality="0..1" />\r
+\r
+       <reference id="nodeRepository" interface="javax.jcr.Repository"\r
+               filter="(argeo.jcr.repository.alias=node)" />\r
+\r
+       <reference id="defaultCallbackHandler" interface="javax.security.auth.callback.CallbackHandler" />\r
+\r
+       <!-- SERVICES -->\r
+       <service interface="org.argeo.security.crypto.CryptoKeyring"\r
+               ref="keyring" />\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui/META-INF/spring/views.xml b/trunk/security/plugins/org.argeo.security.ui/META-INF/spring/views.xml
new file mode 100644 (file)
index 0000000..e5363dc
--- /dev/null
@@ -0,0 +1,17 @@
+<?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="logView" class="org.argeo.security.ui.views.LogView"
+               scope="prototype">
+               <property name="argeoLogger" ref="secureLogger" />
+       </bean>
+
+       <bean id="adminLogView" class="org.argeo.security.ui.views.AdminLogView"
+               scope="prototype">
+               <property name="argeoLogger" ref="secureLogger" />
+       </bean>
+
+</beans>
diff --git a/trunk/security/plugins/org.argeo.security.ui/build.properties b/trunk/security/plugins/org.argeo.security.ui/build.properties
new file mode 100644 (file)
index 0000000..3868784
--- /dev/null
@@ -0,0 +1,6 @@
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml,\
+               icons/
diff --git a/trunk/security/plugins/org.argeo.security.ui/icons/adminLog.gif b/trunk/security/plugins/org.argeo.security.ui/icons/adminLog.gif
new file mode 100644 (file)
index 0000000..6ef3bca
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui/icons/adminLog.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui/icons/home.gif b/trunk/security/plugins/org.argeo.security.ui/icons/home.gif
new file mode 100644 (file)
index 0000000..fd0c669
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui/icons/home.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui/icons/log.gif b/trunk/security/plugins/org.argeo.security.ui/icons/log.gif
new file mode 100644 (file)
index 0000000..e3ecc55
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui/icons/log.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui/icons/maintenance.gif b/trunk/security/plugins/org.argeo.security.ui/icons/maintenance.gif
new file mode 100644 (file)
index 0000000..e5690ec
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui/icons/maintenance.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui/icons/password.gif b/trunk/security/plugins/org.argeo.security.ui/icons/password.gif
new file mode 100644 (file)
index 0000000..a6b251f
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui/icons/password.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui/icons/user.gif b/trunk/security/plugins/org.argeo.security.ui/icons/user.gif
new file mode 100644 (file)
index 0000000..90a0014
Binary files /dev/null and b/trunk/security/plugins/org.argeo.security.ui/icons/user.gif differ
diff --git a/trunk/security/plugins/org.argeo.security.ui/plugin.xml b/trunk/security/plugins/org.argeo.security.ui/plugin.xml
new file mode 100644 (file)
index 0000000..cb139d7
--- /dev/null
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+   <!-- Security -->
+   <extension
+         id="defaultLoginDialog"
+         name="Default Login Dialog"
+         point="org.eclipse.equinox.security.callbackHandler">
+      <callbackHandler
+            class="org.argeo.security.ui.dialogs.DefaultLoginDialog">
+      </callbackHandler>
+   </extension>
+   <extension point="org.eclipse.ui.services">
+        <sourceProvider
+              provider="org.argeo.security.ui.RolesSourceProvider">
+           <variable
+                 name="org.argeo.security.ui.rolesVariable"
+                 priorityLevel="workbench">
+           </variable>
+        </sourceProvider>
+     </extension>
+   <extension
+         point="org.eclipse.ui.views">
+      <view
+            id="org.argeo.security.ui.userProfile"
+            class="org.argeo.security.ui.views.UserProfile"
+            icon="icons/user.gif"
+            name="Profile"
+            restorable="true">
+      </view>
+      <view
+            id="org.argeo.security.ui.logView"
+            class="org.argeo.eclipse.spring.SpringExtensionFactory"
+            name="Log"
+            icon="icons/log.gif"
+            restorable="true">
+      </view>
+      <view
+            id="org.argeo.security.ui.adminLogView"
+            class="org.argeo.eclipse.spring.SpringExtensionFactory"
+            name="Admin Log"
+            icon="icons/adminLog.gif"
+            restorable="true">
+      </view>
+   </extension>
+   <extension
+         point="org.eclipse.ui.perspectives">
+      <perspective
+            class="org.argeo.security.ui.UserHomePerspective"
+            icon="icons/home.gif"
+            id="org.argeo.security.ui.userHomePerspective"
+            name="Home">
+      </perspective>
+      <perspective
+            class="org.argeo.security.ui.MaintenancePerspective"
+            icon="icons/maintenance.gif"
+            id="org.argeo.security.ui.adminMaintenancePerspective"
+            name="Maintenance">
+      </perspective>
+   </extension>
+  <extension
+           point="org.eclipse.ui.activities">
+        <activity
+              description="Admins"
+              id="org.argeo.security.ui.adminActivity"
+              name="Admin">
+                 <enabledWhen>
+                   <with variable="roles">
+                     <iterate ifEmpty="false" operator="or">
+                       <equals value="ROLE_ADMIN" />
+                     </iterate>
+                   </with>
+                 </enabledWhen>
+        </activity>
+        <activity
+              description="Non admins"
+              id="org.argeo.security.ui.notAdminActivity"
+              name="Not Admin">
+                 <enabledWhen>
+                       <not>
+                   <with variable="roles">
+                     <iterate ifEmpty="false" operator="or">
+                       <equals value="ROLE_ADMIN" />
+                     </iterate>
+                   </with>
+                       </not>
+                 </enabledWhen>
+        </activity>
+        <activity
+              description="Non remote"
+              id="org.argeo.security.ui.notRemoteActivity"
+              name="NonRemote">
+                 <enabledWhen>
+                       <not>
+                   <with variable="roles">
+                     <iterate ifEmpty="false" operator="or">
+                       <equals value="ROLE_REMOTE" />
+                     </iterate>
+                   </with>
+                       </not>
+                 </enabledWhen>
+        </activity>
+        <activityPatternBinding
+              activityId="org.argeo.security.ui.adminActivity"
+              isEqualityPattern="true"
+              pattern="org.argeo.security.ui/org.argeo.security.ui.adminMaintenancePerspective">
+        </activityPatternBinding>
+        <activityPatternBinding
+              activityId="org.argeo.security.ui.adminActivity"
+              isEqualityPattern="true"
+              pattern="org.argeo.security.ui/org.argeo.security.ui.adminLogView">
+        </activityPatternBinding>
+     </extension>
+ </plugin>
diff --git a/trunk/security/plugins/org.argeo.security.ui/pom.xml b/trunk/security/plugins/org.argeo.security.ui/pom.xml
new file mode 100644 (file)
index 0000000..3f9b32e
--- /dev/null
@@ -0,0 +1,91 @@
+<?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.security</groupId>
+               <version>2.1.11</version>
+               <artifactId>plugins</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.ui</artifactId>
+       <name>Commons Security UI</name>
+       <packaging>jar</packaging>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-ActivationPolicy>lazy</Bundle-ActivationPolicy>
+                                               <Bundle-Activator>org.argeo.security.ui.SecurityUiPlugin</Bundle-Activator>
+                                               <Require-Bundle>org.eclipse.ui;resolution:=optional,org.eclipse.rap.ui;resolution:=optional,org.eclipse.core.runtime</Require-Bundle>
+                                               <Import-Package>
+                                                       org.argeo.eclipse.spring,
+                                                       org.apache.log4j;resolution:=optional,
+                                                       *
+                                               </Import-Package>
+                                               <Export-Package>
+                                                       !org.argeo.security.ui.internal.*,
+                                                       org.argeo.security.ui.*
+                                               </Export-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- Argeo Security -->
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.core</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Argeo Eclipse -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- RCP only dependency, needed at compile time -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.dep.rcp</artifactId>
+                       <version>2.1.11</version>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- Commons -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.security.core</artifactId>
+               </dependency>
+
+               <!-- Others -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui/securityui.properties b/trunk/security/plugins/org.argeo.security.ui/securityui.properties
new file mode 100644 (file)
index 0000000..0228d47
--- /dev/null
@@ -0,0 +1 @@
+argeo.keyring.secreteKeyLength=256
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/MaintenancePerspective.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/MaintenancePerspective.java
new file mode 100644 (file)
index 0000000..86307ab
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import org.argeo.security.ui.views.AdminLogView;
+import org.argeo.security.ui.views.UserProfile;
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+
+/** Home perspective for the current user */
+public class MaintenancePerspective implements IPerspectiveFactory {
+       public final static String ID = SecurityUiPlugin.PLUGIN_ID
+                       + ".adminMaintenancePerspective";
+
+       public void createInitialLayout(IPageLayout layout) {
+               String editorArea = layout.getEditorArea();
+               layout.setEditorAreaVisible(true);
+               layout.setFixed(false);
+
+               IFolderLayout bottom = layout.createFolder("bottom",
+                               IPageLayout.BOTTOM, 0.50f, editorArea);
+               bottom.addView(AdminLogView.ID);
+
+               IFolderLayout left = layout.createFolder("left", IPageLayout.LEFT,
+                               0.30f, editorArea);
+               left.addView(UserProfile.ID);
+               // left.addView(RolesView.ID);
+
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/PrivilegedJob.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/PrivilegedJob.java
new file mode 100644 (file)
index 0000000..1ded50f
--- /dev/null
@@ -0,0 +1,46 @@
+package org.argeo.security.ui;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.security.auth.Subject;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+import org.springframework.security.Authentication;
+import org.springframework.security.context.SecurityContextHolder;
+
+/**
+ * Propagate authentication to an eclipse job. Typically to execute a privileged
+ * action outside the UI thread
+ */
+public abstract class PrivilegedJob extends Job {
+
+       private final Authentication authentication;
+       private Subject subject;
+
+       public PrivilegedJob(String jobName) {
+               super(jobName);
+               authentication = SecurityContextHolder.getContext().getAuthentication();
+               subject = Subject.getSubject(AccessController.getContext());
+       }
+
+       @Override
+       protected IStatus run(final IProgressMonitor progressMonitor) {
+               PrivilegedAction<IStatus> privilegedAction = new PrivilegedAction<IStatus>() {
+                       public IStatus run() {
+                               SecurityContextHolder.getContext().setAuthentication(
+                                               authentication);
+                               return doRun(progressMonitor);
+                       }
+               };
+               return Subject.doAs(subject, privilegedAction);
+       }
+
+       /**
+        * Implement here what should be executed with default context
+        * authentication
+        */
+       protected abstract IStatus doRun(IProgressMonitor progressMonitor);
+}
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/RolesSourceProvider.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/RolesSourceProvider.java
new file mode 100644 (file)
index 0000000..a81dc20
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.argeo.security.ui.internal.CurrentUser;
+import org.eclipse.ui.AbstractSourceProvider;
+
+/**
+ * Provides the roles of the current user as a variable to be used for activity
+ * binding
+ */
+public class RolesSourceProvider extends AbstractSourceProvider {
+       public final static String ROLES_VARIABLE = "roles";
+       private final static String[] PROVIDED_SOURCE_NAMES = new String[] { ROLES_VARIABLE };
+
+       public Map<String, Set<String>> getCurrentState() {
+               Map<String, Set<String>> stateMap = new HashMap<String, Set<String>>();
+               stateMap.put(ROLES_VARIABLE, CurrentUser.roles());
+               return stateMap;
+       }
+
+       public String[] getProvidedSourceNames() {
+               return PROVIDED_SOURCE_NAMES;
+       }
+
+       public void updateRoles() {
+               fireSourceChanged(0, getCurrentState());
+       }
+
+       public void dispose() {
+       }
+}
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/SecurityUiPlugin.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/SecurityUiPlugin.java
new file mode 100644 (file)
index 0000000..0358418
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * 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;
+
+import java.io.IOException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.argeo.ArgeoException;
+import org.argeo.security.ui.dialogs.DefaultLoginDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class SecurityUiPlugin extends AbstractUIPlugin {
+
+       // The plug-in ID
+       public static final String PLUGIN_ID = "org.argeo.security.ui"; //$NON-NLS-1$
+
+       public final static String CONTEXT_KEYRING = "KEYRING";
+
+       private CallbackHandler defaultCallbackHandler;
+       private ServiceRegistration defaultCallbackHandlerReg;
+
+       private static SecurityUiPlugin plugin;
+
+       public static InheritableThreadLocal<Display> display = new InheritableThreadLocal<Display>() {
+
+               @Override
+               protected Display initialValue() {
+                       return Display.getCurrent();
+               }
+       };
+
+       public void start(BundleContext context) throws Exception {
+               super.start(context);
+               plugin = this;
+
+               defaultCallbackHandler = new DefaultCallbackHandler();
+               defaultCallbackHandlerReg = context.registerService(
+                               CallbackHandler.class.getName(), defaultCallbackHandler, null);
+       }
+
+       public void stop(BundleContext context) throws Exception {
+               plugin = null;
+               defaultCallbackHandlerReg.unregister();
+               super.stop(context);
+       }
+
+       /**
+        * Returns the shared instance
+        * 
+        * @return the shared instance
+        */
+       public static SecurityUiPlugin getDefault() {
+               return plugin;
+       }
+
+       public static ImageDescriptor getImageDescriptor(String path) {
+               return imageDescriptorFromPlugin(PLUGIN_ID, path);
+       }
+
+       protected class DefaultCallbackHandler implements CallbackHandler {
+               public void handle(final Callback[] callbacks) throws IOException,
+                               UnsupportedCallbackException {
+
+                       // if (display != null) // RCP
+                       Display displayToUse = display.get();
+                       if (displayToUse == null)// RCP
+                               displayToUse = Display.getDefault();
+                       displayToUse.syncExec(new Runnable() {
+                               public void run() {
+                                       DefaultLoginDialog dialog = new DefaultLoginDialog(display
+                                                       .get().getActiveShell());
+                                       try {
+                                               dialog.handle(callbacks);
+                                       } catch (IOException e) {
+                                               throw new ArgeoException("Cannot open dialog", e);
+                                       }
+                               }
+                       });
+                       // else {// RAP
+                       // DefaultLoginDialog dialog = new DefaultLoginDialog();
+                       // dialog.handle(callbacks);
+                       // }
+               }
+
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/UserHomePerspective.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/UserHomePerspective.java
new file mode 100644 (file)
index 0000000..119549f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import org.argeo.security.ui.views.LogView;
+import org.argeo.security.ui.views.UserProfile;
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+
+/** Home perspective for the current user */
+public class UserHomePerspective implements IPerspectiveFactory {
+       public final static String ID = SecurityUiPlugin.PLUGIN_ID
+                       + ".userHomePerspective";
+
+       public void createInitialLayout(IPageLayout layout) {
+               String editorArea = layout.getEditorArea();
+               layout.setEditorAreaVisible(true);
+               layout.setFixed(false);
+
+               IFolderLayout left = layout.createFolder("left", IPageLayout.LEFT,
+                               0.30f, editorArea);
+               left.addView(UserProfile.ID);
+               left.addView(LogView.ID);
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/commands/OpenChangePasswordDialog.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/commands/OpenChangePasswordDialog.java
new file mode 100644 (file)
index 0000000..3044e2c
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.commands;
+
+import org.argeo.security.ui.dialogs.ChangePasswordDialog;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.springframework.security.userdetails.UserDetailsManager;
+
+/** Opens the change password dialog. */
+public class OpenChangePasswordDialog extends AbstractHandler {
+       private UserDetailsManager userDetailsManager;
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               ChangePasswordDialog dialog = new ChangePasswordDialog(
+                               HandlerUtil.getActiveShell(event), userDetailsManager);
+               if (dialog.open() == Dialog.OK) {
+                       MessageDialog.openInformation(HandlerUtil.getActiveShell(event),
+                                       "Password changed", "Password changed.");
+               }
+               return null;
+       }
+
+       public void setUserDetailsManager(UserDetailsManager userDetailsManager) {
+               this.userDetailsManager = userDetailsManager;
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/commands/OpenHomePerspective.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/commands/OpenHomePerspective.java
new file mode 100644 (file)
index 0000000..d56498a
--- /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.security.ui.commands;
+
+import org.argeo.eclipse.ui.ErrorFeedback;
+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 OpenHomePerspective extends AbstractHandler {
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               try {
+                       HandlerUtil.getActiveSite(event).getWorkbenchWindow()
+                                       .openPage(UserHomePerspective.ID, null);
+               } catch (WorkbenchException e) {
+                       ErrorFeedback.show("Cannot open home perspective", e);
+               }
+               return null;
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/dialogs/AbstractLoginDialog.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/dialogs/AbstractLoginDialog.java
new file mode 100644 (file)
index 0000000..7c7e0c6
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * 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.dialogs;
+
+import java.io.IOException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.security.ui.SecurityUiPlugin;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TrayDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.operation.ModalContext;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/** Base for login dialogs */
+public abstract class AbstractLoginDialog extends TrayDialog implements
+               CallbackHandler {
+
+       private final static Log log = LogFactory.getLog(AbstractLoginDialog.class);
+
+       private Thread modalContextThread = null;
+       boolean processCallbacks = false;
+       boolean isCancelled = false;
+       Callback[] callbackArray;
+
+       protected final Callback[] getCallbacks() {
+               return this.callbackArray;
+       }
+
+       public abstract void internalHandle();
+
+       public boolean isCancelled() {
+               return isCancelled;
+       }
+
+       protected AbstractLoginDialog(Shell parentShell) {
+               super(parentShell);
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see
+        * javax.security.auth.callback.CallbackHandler#handle(javax.security.auth
+        * .callback.Callback[])
+        */
+       public void handle(final Callback[] callbacks) throws IOException {
+               // clean previous usage
+               if (processCallbacks) {
+                       // this handler was already used
+                       processCallbacks = false;
+               }
+
+               if (modalContextThread != null) {
+                       try {
+                               modalContextThread.join(1000);
+                       } catch (InterruptedException e) {
+                               // silent
+                       }
+                       modalContextThread = null;
+               }
+
+               // initialize
+               this.callbackArray = callbacks;
+               final Display display = Display.getDefault();
+               display.syncExec(new Runnable() {
+
+                       public void run() {
+                               isCancelled = false;
+                               setBlockOnOpen(false);
+                               open();
+
+                               final Button okButton = getButton(IDialogConstants.OK_ID);
+                               okButton.setText("Login");
+                               okButton.addSelectionListener(new SelectionListener() {
+
+                                       public void widgetSelected(final SelectionEvent event) {
+                                               processCallbacks = true;
+                                       }
+
+                                       public void widgetDefaultSelected(final SelectionEvent event) {
+                                               // nothing to do
+                                       }
+                               });
+                               final Button cancel = getButton(IDialogConstants.CANCEL_ID);
+                               cancel.addSelectionListener(new SelectionListener() {
+
+                                       public void widgetSelected(final SelectionEvent event) {
+                                               isCancelled = true;
+                                               processCallbacks = true;
+                                       }
+
+                                       public void widgetDefaultSelected(final SelectionEvent event) {
+                                               // nothing to do
+                                       }
+                               });
+                       }
+               });
+               try {
+                       ModalContext.setAllowReadAndDispatch(true); // Works for now.
+                       ModalContext.run(new IRunnableWithProgress() {
+
+                               public void run(final IProgressMonitor monitor) {
+                                       modalContextThread = Thread.currentThread();
+                                       // Wait here until OK or cancel is pressed, then let it rip.
+                                       // The event
+                                       // listener
+                                       // is responsible for closing the dialog (in the
+                                       // loginSucceeded
+                                       // event).
+                                       while (!processCallbacks && (modalContextThread != null)
+                                                       && (modalContextThread == Thread.currentThread())
+                                                       && SecurityUiPlugin.getDefault() != null) {
+                                               // Note: SecurityUiPlugin.getDefault() != null is false
+                                               // when the OSGi runtime is shut down
+                                               try {
+                                                       Thread.sleep(100);
+                                                       // if (display.isDisposed()) {
+                                                       // log.warn("Display is disposed, killing login dialog thread");
+                                                       // throw new ThreadDeath();
+                                                       // }
+                                               } catch (final Exception e) {
+                                                       // do nothing
+                                               }
+                                       }
+                                       processCallbacks = false;
+                                       // Call the adapter to handle the callbacks
+                                       if (!isCancelled())
+                                               internalHandle();
+                                       else
+                                               // clear callbacks are when cancelling
+                                               for (Callback callback : callbacks)
+                                                       if (callback instanceof PasswordCallback)
+                                                               ((PasswordCallback) callback).setPassword(null);
+                                                       else if (callback instanceof NameCallback)
+                                                               ((NameCallback) callback).setName(null);
+                               }
+                       }, true, new NullProgressMonitor(), Display.getDefault());
+               } catch (ThreadDeath e) {
+                       isCancelled = true;
+                       log.debug("Thread " + Thread.currentThread().getId() + " died");
+                       throw e;
+               } catch (Exception e) {
+                       isCancelled = true;
+                       IOException ioe = new IOException(
+                                       "Unexpected issue in login dialog, see root cause for more details");
+                       ioe.initCause(e);
+                       throw ioe;
+               } finally {
+                       // so that the modal thread dies
+                       processCallbacks = true;
+                       // try {
+                       // // wait for the modal context thread to gracefully exit
+                       // modalContextThread.join();
+                       // } catch (InterruptedException ie) {
+                       // // silent
+                       // }
+                       modalContextThread = null;
+               }
+       }
+
+       protected void configureShell(Shell shell) {
+               super.configureShell(shell);
+               shell.setText("Authentication");
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/dialogs/ChangePasswordDialog.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/dialogs/ChangePasswordDialog.java
new file mode 100644 (file)
index 0000000..fe9b6ae
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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.dialogs;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.springframework.security.userdetails.UserDetailsManager;
+
+/** Dialog to change the current user password */
+public class ChangePasswordDialog extends TitleAreaDialog {
+       private Text currentPassword, newPassword1, newPassword2;
+       private UserDetailsManager userDetailsManager;
+
+       public ChangePasswordDialog(Shell parentShell,
+                       UserDetailsManager securityService) {
+               super(parentShell);
+               this.userDetailsManager = securityService;
+       }
+
+       protected Point getInitialSize() {
+               return new Point(300, 250);
+       }
+
+       protected Control createDialogArea(Composite parent) {
+               Composite dialogarea = (Composite) super.createDialogArea(parent);
+               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               Composite composite = new Composite(dialogarea, SWT.NONE);
+               composite.setLayout(new GridLayout(2, false));
+               composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               currentPassword = createLP(composite, "Current password");
+               newPassword1 = createLP(composite, "New password");
+               newPassword2 = createLP(composite, "Repeat new password");
+
+               setMessage("Change password", IMessageProvider.INFORMATION);
+               parent.pack();
+               return composite;
+       }
+
+       @Override
+       protected void okPressed() {
+               if (!newPassword1.getText().equals(newPassword2.getText()))
+                       throw new ArgeoException("Passwords are different");
+               try {
+                       userDetailsManager.changePassword(currentPassword.getText(),
+                                       newPassword1.getText());
+                       close();
+               } catch (Exception e) {
+                       ErrorFeedback.show("Cannot change password", e);
+               }
+       }
+
+       /** Creates label and password. */
+       protected Text createLP(Composite parent, String label) {
+               new Label(parent, SWT.NONE).setText(label);
+               Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.PASSWORD
+                               | SWT.BORDER);
+               text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               return text;
+       }
+
+       protected void configureShell(Shell shell) {
+               super.configureShell(shell);
+               shell.setText("Change password");
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/dialogs/DefaultLoginDialog.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/dialogs/DefaultLoginDialog.java
new file mode 100644 (file)
index 0000000..57ba01b
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * 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.dialogs;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.TextOutputCallback;
+
+import org.argeo.security.ui.SecurityUiPlugin;
+import org.argeo.util.LocaleCallback;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+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.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/** Default authentication dialog, to be used as {@link CallbackHandler}. */
+public class DefaultLoginDialog extends AbstractLoginDialog {
+       public DefaultLoginDialog() {
+               this(SecurityUiPlugin.display.get().getActiveShell());
+       }
+
+       public DefaultLoginDialog(Shell parentShell) {
+               super(parentShell);
+       }
+
+       protected Point getInitialSize() {
+               return new Point(350, 180);
+       }
+
+       @Override
+       protected Control createContents(Composite parent) {
+               Control control = super.createContents(parent);
+               parent.pack();
+
+               // Move the dialog to the center of the top level shell.
+               Rectangle shellBounds;
+               if (Display.getCurrent().getActiveShell() != null) // RCP
+                       shellBounds = Display.getCurrent().getActiveShell().getBounds();
+               else
+                       shellBounds = Display.getCurrent().getBounds();// RAP
+               Point dialogSize = parent.getSize();
+               int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2;
+               int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2;
+               parent.setLocation(x, y);
+               return control;
+       }
+
+       protected Control createDialogArea(Composite parent) {
+               Composite dialogarea = (Composite) super.createDialogArea(parent);
+               Composite composite = new Composite(dialogarea, SWT.NONE);
+               composite.setLayout(new GridLayout(2, false));
+               composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               createCallbackHandlers(composite);
+               // parent.pack();
+               return composite;
+       }
+
+       private void createCallbackHandlers(Composite composite) {
+               Callback[] callbacks = getCallbacks();
+               for (int i = 0; i < callbacks.length; i++) {
+                       Callback callback = callbacks[i];
+                       if (callback instanceof TextOutputCallback) {
+                               createLabelTextoutputHandler(composite,
+                                               (TextOutputCallback) callback);
+                       } else if (callback instanceof NameCallback) {
+                               createNameHandler(composite, (NameCallback) callback);
+                       } else if (callback instanceof PasswordCallback) {
+                               createPasswordHandler(composite, (PasswordCallback) callback);
+                       } else if (callback instanceof LocaleCallback) {
+                               createLocaleHandler(composite, (LocaleCallback) callback);
+                       }
+               }
+       }
+
+       private void createPasswordHandler(Composite composite,
+                       final PasswordCallback callback) {
+               Label label = new Label(composite, SWT.NONE);
+               label.setText(callback.getPrompt());
+               final Text passwordText = new Text(composite, SWT.SINGLE | SWT.LEAD
+                               | SWT.PASSWORD | SWT.BORDER);
+               passwordText
+                               .setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               passwordText.addModifyListener(new ModifyListener() {
+
+                       public void modifyText(ModifyEvent event) {
+                               // FIXME use getTextChars() in Eclipse 3.7
+                               callback.setPassword(passwordText.getText().toCharArray());
+                       }
+               });
+       }
+
+       private void createLocaleHandler(Composite composite,
+                       final LocaleCallback callback) {
+               String[] labels = callback.getSupportedLocalesLabels();
+               if (labels.length == 0)
+                       return;
+               Label label = new Label(composite, SWT.NONE);
+               label.setText(callback.getPrompt());
+
+               final Combo combo = new Combo(composite, SWT.READ_ONLY);
+               combo.setItems(labels);
+               combo.select(callback.getDefaultIndex());
+               combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               combo.addSelectionListener(new SelectionListener() {
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               callback.setSelectedIndex(combo.getSelectionIndex());
+                       }
+
+                       @Override
+                       public void widgetDefaultSelected(SelectionEvent e) {
+                       }
+               });
+       }
+
+       private void createNameHandler(Composite composite,
+                       final NameCallback callback) {
+               Label label = new Label(composite, SWT.NONE);
+               label.setText(callback.getPrompt());
+               final Text text = new Text(composite, SWT.SINGLE | SWT.LEAD
+                               | SWT.BORDER);
+               if (callback.getDefaultName() != null) {
+                       // set default value, if provided
+                       text.setText(callback.getDefaultName());
+                       callback.setName(callback.getDefaultName());
+               }
+               text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               text.addModifyListener(new ModifyListener() {
+
+                       public void modifyText(ModifyEvent event) {
+                               callback.setName(text.getText());
+                       }
+               });
+       }
+
+       private void createLabelTextoutputHandler(Composite composite,
+                       final TextOutputCallback callback) {
+               Label label = new Label(composite, SWT.NONE);
+               label.setText(callback.getMessage());
+               GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+               data.horizontalSpan = 2;
+               label.setLayoutData(data);
+               // TODO: find a way to pass this information
+               // int messageType = callback.getMessageType();
+               // int dialogMessageType = IMessageProvider.NONE;
+               // switch (messageType) {
+               // case TextOutputCallback.INFORMATION:
+               // dialogMessageType = IMessageProvider.INFORMATION;
+               // break;
+               // case TextOutputCallback.WARNING:
+               // dialogMessageType = IMessageProvider.WARNING;
+               // break;
+               // case TextOutputCallback.ERROR:
+               // dialogMessageType = IMessageProvider.ERROR;
+               // break;
+               // }
+               // setMessage(callback.getMessage(), dialogMessageType);
+       }
+
+       public void internalHandle() {
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/internal/CurrentUser.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/internal/CurrentUser.java
new file mode 100644 (file)
index 0000000..43ca588
--- /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.security.ui.internal;
+
+import java.security.AccessController;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+
+import org.argeo.ArgeoException;
+import org.springframework.security.Authentication;
+import org.springframework.security.GrantedAuthority;
+
+/**
+ * Retrieves information about the current user. Not an API, can change without
+ * notice.
+ */
+public class CurrentUser {
+       public final static String getUsername() {
+               Subject subject = getSubject();
+               if (subject == null)
+                       return null;
+               Principal principal = subject.getPrincipals().iterator().next();
+               return principal.getName();
+
+       }
+
+       public final static Set<String> roles() {
+               Set<String> roles = Collections.synchronizedSet(new HashSet<String>());
+               Authentication authentication = getAuthentication();
+               for (GrantedAuthority ga : authentication.getAuthorities()) {
+                       roles.add(ga.getAuthority());
+               }
+               return Collections.unmodifiableSet(roles);
+       }
+
+       public final static Authentication getAuthentication() {
+               Set<Authentication> authens = getSubject().getPrincipals(
+                               Authentication.class);
+               if (authens != null && !authens.isEmpty()) {
+                       Principal principal = authens.iterator().next();
+                       Authentication authentication = (Authentication) principal;
+                       return authentication;
+               }
+               throw new ArgeoException("No authentication found");
+       }
+
+       public final static Subject getSubject() {
+               Subject subject = Subject.getSubject(AccessController.getContext());
+               if (subject == null)
+                       throw new ArgeoException("Not authenticated.");
+               return subject;
+       }
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/views/AdminLogView.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/views/AdminLogView.java
new file mode 100644 (file)
index 0000000..d59edf8
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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.views;
+
+import java.util.ArrayList;
+
+import org.argeo.ArgeoLogger;
+import org.argeo.security.ui.SecurityUiPlugin;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.part.ViewPart;
+
+/**
+ * Display log lines for all users with a virtual table.
+ */
+public class AdminLogView extends ViewPart {
+       public static String ID = SecurityUiPlugin.PLUGIN_ID + ".adminLogView";
+
+       private TableViewer viewer;
+
+       private LogContentProvider logContentProvider;
+       private ArgeoLogger argeoLogger;
+
+       @Override
+       public void createPartControl(Composite parent) {
+               // FIXME doesn't return a monospace font in RAP
+               Font font = JFaceResources.getTextFont();
+               Table table = new Table(parent, SWT.VIRTUAL | SWT.MULTI | SWT.H_SCROLL
+                               | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
+               table.setFont(font);
+
+               viewer = new TableViewer(table);
+               viewer.setLabelProvider(new LabelProvider());
+               logContentProvider = new LogContentProvider(viewer) {
+
+                       @Override
+                       protected StringBuffer prefix(String username, Long timestamp,
+                                       String level, String category, String thread) {
+                               return super
+                                               .prefix(username, timestamp, level, category, thread)
+                                               .append(norm(level, 5))
+                                               .append(' ')
+                                               .append(norm(username != null ? username
+                                                               : "<anonymous>", 16)).append(' ');
+                       }
+               };
+               viewer.setContentProvider(logContentProvider);
+               // viewer.setUseHashlookup(true);
+               viewer.setInput(new ArrayList<String>());
+
+               if (argeoLogger != null)
+                       argeoLogger.registerForAll(logContentProvider, 1000, true);
+       }
+
+       @Override
+       public void setFocus() {
+               viewer.getTable().setFocus();
+       }
+
+       @Override
+       public void dispose() {
+               if (argeoLogger != null)
+                       argeoLogger.unregisterForAll(logContentProvider);
+       }
+
+       public void setArgeoLogger(ArgeoLogger argeoLogger) {
+               this.argeoLogger = argeoLogger;
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/views/LogContentProvider.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/views/LogContentProvider.java
new file mode 100644 (file)
index 0000000..72f2059
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * 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.views;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.argeo.ArgeoLogListener;
+import org.eclipse.jface.viewers.ILazyContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+/** A content provider maintaining an array of lines */
+class LogContentProvider implements ILazyContentProvider, ArgeoLogListener {
+       private DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
+
+       private final Long start;
+       /** current - start = line number. first line is number '1' */
+       private Long current;
+
+       // TODO make it configurable
+       private final Integer maxLineBufferSize = 10 * 1000;
+
+       private final TableViewer viewer;
+       private LinkedList<LogLine> lines;
+
+       public LogContentProvider(TableViewer viewer) {
+               this.viewer = viewer;
+               start = System.currentTimeMillis();
+               lines = new LinkedList<LogLine>();
+               current = start;
+       }
+
+       public synchronized void dispose() {
+               lines.clear();
+               lines = null;
+       }
+
+       @SuppressWarnings("unchecked")
+       public synchronized void inputChanged(Viewer viewer, Object oldInput,
+                       Object newInput) {
+               List<String> lin = (List<String>) newInput;
+               if (lin == null)
+                       return;
+               for (String line : lin) {
+                       addLine(line);
+               }
+               this.viewer.setItemCount(lines.size());
+       }
+
+       public void updateElement(int index) {
+               viewer.replace(lines.get(index), index);
+       }
+
+       public synchronized void appendLog(String username, Long timestamp,
+                       String level, String category, String thread, Object msg,
+                       String[] exception) {
+               // check if valid
+               if (lines == null)
+                       return;
+
+               String message = msg.toString();
+               int count = 0;
+               String prefix = prefix(username, timestamp, level, category, thread)
+                               .toString();
+               // String suffix = suffix(username, timestamp, level, category, thread);
+               for (String line : message.split("\n")) {
+                       addLine(count == 0 ? prefix + line : line);
+                       count++;
+               }
+
+               if (exception != null) {
+                       for (String ste : exception) {
+                               addLine(ste);
+                       }
+               }
+
+               viewer.getTable().getDisplay().asyncExec(new Runnable() {
+                       public void run() {
+                               if (lines == null)
+                                       return;
+                               viewer.setItemCount(lines.size());
+                               // doesn't work with syncExec
+                               scrollToLastLine();
+                       }
+               });
+       }
+
+       protected StringBuffer prefix(String username, Long timestamp,
+                       String level, String category, String thread) {
+               StringBuffer buf = new StringBuffer("");
+               buf.append(dateFormat.format(new Date(timestamp))).append(" ");
+               // buf.append(level).append(" ");
+               return buf;
+       }
+
+       /** Normalize string to the given size */
+       protected String norm(String str, Integer size) {
+               int length = str.length();
+               if (length == size)
+                       return str;
+               else if (length > size)
+                       return str.substring(0, size);
+               else {
+                       char[] arr = new char[size - length];
+                       Arrays.fill(arr, ' ');
+                       return str + new String(arr);
+               }
+       }
+
+       // protected String suffix(String username, Long timestamp, String level,
+       // String category, String thread) {
+       // return "";
+       // }
+
+       /** Scroll to the last line */
+       protected synchronized void scrollToLastLine() {
+               // we try to show last line with two methods
+               // viewer.reveal(lines.peekLast());
+
+               Table table = viewer.getTable();
+               TableItem ti = table.getItem(table.getItemCount() - 1);
+               table.showItem(ti);
+       }
+
+       protected synchronized LogLine addLine(String line) {
+               // check for maximal size and purge if necessary
+               while (lines.size() >= maxLineBufferSize) {
+                       for (int i = 0; i < maxLineBufferSize / 10; i++) {
+                               lines.poll();
+                       }
+               }
+
+               current++;
+               LogLine logLine = new LogLine(current, line);
+               lines.add(logLine);
+               return logLine;
+       }
+
+       private class LogLine {
+               private Long linenumber;
+               private String message;
+
+               public LogLine(Long linenumber, String message) {
+                       this.linenumber = linenumber;
+                       this.message = message;
+               }
+
+               @Override
+               public int hashCode() {
+                       return linenumber.intValue();
+               }
+
+               @Override
+               public boolean equals(Object obj) {
+                       if (obj instanceof LogLine)
+                               return ((LogLine) obj).linenumber.equals(linenumber);
+                       else
+                               return false;
+               }
+
+               @Override
+               public String toString() {
+                       return message;
+               }
+
+       }
+}
\ No newline at end of file
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/views/LogView.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/views/LogView.java
new file mode 100644 (file)
index 0000000..c7c47b3
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.views;
+
+import java.util.ArrayList;
+
+import org.argeo.ArgeoLogListener;
+import org.argeo.ArgeoLogger;
+import org.argeo.security.ui.SecurityUiPlugin;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.part.ViewPart;
+
+/**
+ * Display log lines with a virtual table. Register and unregisters a
+ * {@link ArgeoLogListener} via OSGi services.
+ */
+public class LogView extends ViewPart {
+       public static String ID = SecurityUiPlugin.PLUGIN_ID + ".logView";
+
+       private TableViewer viewer;
+
+       private LogContentProvider logContentProvider;
+       private ArgeoLogger argeoLogger;
+
+       @Override
+       public void createPartControl(Composite parent) {
+               Font font = JFaceResources.getTextFont();
+               Table table = new Table(parent, SWT.VIRTUAL | SWT.MULTI | SWT.H_SCROLL
+                               | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
+               table.setFont(font);
+
+               viewer = new TableViewer(table);
+               viewer.setLabelProvider(new LabelProvider());
+               logContentProvider = new LogContentProvider(viewer);
+               viewer.setContentProvider(logContentProvider);
+               // viewer.setUseHashlookup(true);
+               viewer.setInput(new ArrayList<String>());
+
+               if (argeoLogger != null)
+                       argeoLogger.register(logContentProvider, 1000);
+       }
+
+       @Override
+       public void setFocus() {
+               viewer.getTable().setFocus();
+       }
+
+       @Override
+       public void dispose() {
+               if (argeoLogger != null)
+                       argeoLogger.unregister(logContentProvider);
+       }
+
+       public void setArgeoLogger(ArgeoLogger argeoLogger) {
+               this.argeoLogger = argeoLogger;
+       }
+
+}
diff --git a/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/views/UserProfile.java b/trunk/security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/views/UserProfile.java
new file mode 100644 (file)
index 0000000..3f1f000
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.views;
+
+import java.util.TreeSet;
+
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.security.ui.SecurityUiPlugin;
+import org.argeo.security.ui.internal.CurrentUser;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.part.ViewPart;
+import org.springframework.security.Authentication;
+
+/** Information about the currently logged in user */
+public class UserProfile extends ViewPart {
+       public static String ID = SecurityUiPlugin.PLUGIN_ID + ".userProfile";
+
+       private TableViewer viewer;
+
+       @Override
+       public void createPartControl(Composite parent) {
+               parent.setLayout(new GridLayout(2, false));
+
+               Authentication authentication = CurrentUser.getAuthentication();
+               EclipseUiUtils.createGridLL(parent, "Name", authentication
+                               .getPrincipal().toString());
+               EclipseUiUtils.createGridLL(parent, "User ID",
+                               CurrentUser.getUsername());
+
+               // roles table
+               Table table = new Table(parent, SWT.V_SCROLL | SWT.BORDER);
+               table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+               table.setLinesVisible(false);
+               table.setHeaderVisible(false);
+               viewer = new TableViewer(table);
+               viewer.setContentProvider(new RolesContentProvider());
+               viewer.setLabelProvider(new LabelProvider());
+               getViewSite().setSelectionProvider(viewer);
+               viewer.setInput(getViewSite());
+       }
+
+       @Override
+       public void setFocus() {
+               viewer.getTable();
+       }
+
+       private class RolesContentProvider implements IStructuredContentProvider {
+               public Object[] getElements(Object inputElement) {
+                       return new TreeSet<String>(CurrentUser.roles()).toArray();
+               }
+
+               public void dispose() {
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+
+       }
+
+}
diff --git a/trunk/security/plugins/pom.xml b/trunk/security/plugins/pom.xml
new file mode 100644 (file)
index 0000000..ca9c74b
--- /dev/null
@@ -0,0 +1,44 @@
+<?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.11</version>
+               <artifactId>security</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <groupId>org.argeo.commons.security</groupId>
+       <artifactId>plugins</artifactId>
+       <name>Commons Security Eclipse Plugins</name>
+       <packaging>pom</packaging>
+       <modules>
+               <module>org.argeo.security.equinox</module>
+               <module>org.argeo.security.ui</module>
+               <module>org.argeo.security.ui.admin</module>
+               <module>org.argeo.security.ui.rcp</module>
+               <module>org.argeo.security.ui.rap</module>
+       </modules>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-SymbolicName>${project.artifactId};singleton:=true</Bundle-SymbolicName>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                               <configuration>
+                                       <!-- Prevents source jars to contain misleading data -->
+                                       <excludes>
+                                               <exclude>plugin.xml</exclude>
+                                               <exclude>META-INF/MANIFEST.MF</exclude>
+                                       </excludes>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/security/pom.xml b/trunk/security/pom.xml
new file mode 100644 (file)
index 0000000..73ccbb2
--- /dev/null
@@ -0,0 +1,19 @@
+<?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">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <version>2.1.11</version>
+               <artifactId>argeo-commons</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>security</artifactId>
+       <name>Commons Security</name>
+       <packaging>pom</packaging>
+       <modules>
+               <module>runtime</module>
+               <module>modules</module>
+               <module>plugins</module>
+               <module>dep</module>
+       </modules>
+</project>
\ No newline at end of file
diff --git a/trunk/security/runtime/org.argeo.security.activemq/.classpath b/trunk/security/runtime/org.argeo.security.activemq/.classpath
new file mode 100644 (file)
index 0000000..c0bbc0a
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="src" path="src/main/resources"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/security/runtime/org.argeo.security.activemq/.project b/trunk/security/runtime/org.argeo.security.activemq/.project
new file mode 100644 (file)
index 0000000..cdc841c
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.security.activemq</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/trunk/security/runtime/org.argeo.security.activemq/.settings/org.eclipse.jdt.core.prefs b/trunk/security/runtime/org.argeo.security.activemq/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..312bbe1
--- /dev/null
@@ -0,0 +1,8 @@
+#Wed Feb 16 10:40:27 CET 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/security/runtime/org.argeo.security.activemq/.settings/org.eclipse.pde.core.prefs b/trunk/security/runtime/org.argeo.security.activemq/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..85f2008
--- /dev/null
@@ -0,0 +1,4 @@
+#Wed Feb 16 10:40:27 CET 2011
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/trunk/security/runtime/org.argeo.security.activemq/build.properties b/trunk/security/runtime/org.argeo.security.activemq/build.properties
new file mode 100644 (file)
index 0000000..2c65f86
--- /dev/null
@@ -0,0 +1,5 @@
+source.. = src/main/java/,\
+           src/main/resources/
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .
diff --git a/trunk/security/runtime/org.argeo.security.activemq/pom.xml b/trunk/security/runtime/org.argeo.security.activemq/pom.xml
new file mode 100644 (file)
index 0000000..862d614
--- /dev/null
@@ -0,0 +1,88 @@
+<?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.security</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.activemq</artifactId>
+       <name>Commons Security ActiveMQ</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>
+                                                       org.argeo.security.activemq.*
+                                               </Export-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.core</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.transaction</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.security.core</artifactId>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+
+               <!-- JMS -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.activemq</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.jms</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.jms</artifactId>
+               </dependency>
+
+               <!-- TEST -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>junit</artifactId>
+                       <scope>test</scope>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/security/runtime/org.argeo.security.activemq/src/main/java/org/argeo/security/activemq/ActiveMqSecurityBrokerPlugin.java b/trunk/security/runtime/org.argeo.security.activemq/src/main/java/org/argeo/security/activemq/ActiveMqSecurityBrokerPlugin.java
new file mode 100644 (file)
index 0000000..5ecab67
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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.activemq;
+
+import org.apache.activemq.broker.BrokerPluginSupport;
+import org.apache.activemq.broker.ConnectionContext;
+import org.apache.activemq.command.ConnectionInfo;
+import org.argeo.ArgeoException;
+import org.argeo.security.core.InternalAuthentication;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationManager;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+
+@SuppressWarnings("unchecked")
+/** Integrates Spring Security with ActiveMQ security.*/
+public class ActiveMqSecurityBrokerPlugin extends BrokerPluginSupport {
+       // private final static Log log = LogFactory
+       // .getLog(ActiveMqSecurityBrokerPlugin.class);
+
+       private AuthenticationManager authenticationManager;
+       private String systemUsername = InternalAuthentication.DEFAULT_SYSTEM_USERNAME;
+       private String systemRole = InternalAuthentication.DEFAULT_SYSTEM_ROLE;
+
+       @Override
+       public void addConnection(ConnectionContext context, ConnectionInfo info)
+                       throws Exception {
+               String username = info.getUserName();
+               if (username == null)
+                       throw new ArgeoException("No user name provided");
+               String password = info.getPassword();
+               if (password == null) {
+                       password = context.getConnection().getRemoteAddress().substring(1);
+                       password = password.substring(0, password.lastIndexOf(':'));
+               }
+
+               SecurityContext securityContext = SecurityContextHolder.getContext();
+
+               final Authentication authRequest;
+               if (username.equals(systemUsername))
+                       authRequest = new InternalAuthentication(password, username,
+                                       systemRole);
+               else
+                       authRequest = new UsernamePasswordAuthenticationToken(username,
+                                       password);
+
+               final Authentication auth = authenticationManager
+                               .authenticate(authRequest);
+               securityContext.setAuthentication(auth);
+               context.setSecurityContext(new ActiveMqSpringSecurityContext(
+                               securityContext));
+
+               super.addConnection(context, info);
+       }
+
+       public void setAuthenticationManager(
+                       AuthenticationManager authenticationManager) {
+               this.authenticationManager = authenticationManager;
+       }
+
+       public void setSystemUsername(String systemUsername) {
+               this.systemUsername = systemUsername;
+       }
+
+       public void setSystemRole(String systemRole) {
+               this.systemRole = systemRole;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.activemq/src/main/java/org/argeo/security/activemq/ActiveMqSpringSecurityContext.java b/trunk/security/runtime/org.argeo.security.activemq/src/main/java/org/argeo/security/activemq/ActiveMqSpringSecurityContext.java
new file mode 100644 (file)
index 0000000..f9ba038
--- /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.activemq;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.context.SecurityContext;
+
+/** An ActiveMQ security context compatible with Spring Security. */
+public class ActiveMqSpringSecurityContext extends
+               org.apache.activemq.security.SecurityContext {
+
+       private final SecurityContext springSecurityContext;
+
+       public ActiveMqSpringSecurityContext(SecurityContext springSecurityContext) {
+               super(springSecurityContext.getAuthentication().getName());
+               this.springSecurityContext = springSecurityContext;
+       }
+
+       @Override
+       public Set<?> getPrincipals() {
+               return new HashSet<GrantedAuthority>(
+                               Arrays.asList(springSecurityContext.getAuthentication()
+                                               .getAuthorities()));
+       }
+
+       public SecurityContext getSpringSecurityContext() {
+               return springSecurityContext;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.activemq/src/main/java/org/argeo/security/activemq/SecuredActiveMqConnectionFactory.java b/trunk/security/runtime/org.argeo.security.activemq/src/main/java/org/argeo/security/activemq/SecuredActiveMqConnectionFactory.java
new file mode 100644 (file)
index 0000000..4597dd5
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * 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.activemq;
+
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+import javax.swing.plaf.metal.MetalLookAndFeel;
+
+import org.apache.activemq.ActiveMQSslConnectionFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.core.io.Resource;
+import org.springframework.jms.connection.CachingConnectionFactory;
+import org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter;
+
+/** An ActiveMQ connection factory managing secure connections. */
+public class SecuredActiveMqConnectionFactory implements ConnectionFactory,
+               InitializingBean, DisposableBean {
+
+       public final static String AUTHMODE_UI = "ui";
+       public final static String AUTHMODE_OS = "os";
+       public final static String AUTHMODE_DEFAULT = AUTHMODE_OS;
+       // private final static String LOGIN_CONFIG_PROPERTY =
+       // "java.security.auth.login.config";
+
+       private final static Log log = LogFactory
+                       .getLog(SecuredActiveMqConnectionFactory.class);
+
+       private String keyStorePassword;
+       private Resource keyStore;
+       private String keyStoreType = "JKS";// "PKCS12"
+       private String brokerURL;
+
+       private String authenticationMode;
+
+       private CachingConnectionFactory cachingConnectionFactory;
+
+       public Connection createConnection() throws JMSException {
+               return cachingConnectionFactory.createConnection();
+       }
+
+       public Connection createConnection(String userName, String password)
+                       throws JMSException {
+               throw new UnsupportedOperationException();
+       }
+
+       public void afterPropertiesSet() throws Exception {
+               ActiveMQSslConnectionFactory activeMQSslConnectionFactory = new ActiveMQSslConnectionFactory();
+               prepareActiveMqSslConnectionFactory(activeMQSslConnectionFactory);
+               activeMQSslConnectionFactory.setBrokerURL(brokerURL);
+               UserCredentialsConnectionFactoryAdapter uccfa = new UserCredentialsConnectionFactoryAdapter();
+               uccfa.setTargetConnectionFactory(activeMQSslConnectionFactory);
+               cachingConnectionFactory = new CachingConnectionFactory();
+               cachingConnectionFactory.setTargetConnectionFactory(uccfa);
+               cachingConnectionFactory.setCacheConsumers(false);
+
+               initConnectionFactoryCredentials(uccfa);
+               cachingConnectionFactory.initConnection();
+               log.info("Connected to " + brokerURL);
+               uccfa.setUsername(null);
+               uccfa.setPassword(null);
+
+       }
+
+       protected void initConnectionFactoryCredentials(
+                       final UserCredentialsConnectionFactoryAdapter uccfa) {
+               if (authenticationMode == null)
+                       authenticationMode = AUTHMODE_DEFAULT;
+
+               if (AUTHMODE_OS.equals(authenticationMode)) {
+                       // if (false) {
+                       // // Cache previous value of login conf location
+                       // String oldLoginConfLocation = System
+                       // .getProperty(LOGIN_CONFIG_PROPERTY);
+                       // // Find OS family
+                       // String osName = System.getProperty("os.name");
+                       // final String auth;
+                       // if (osName.startsWith("Windows"))
+                       // auth = "Windows";
+                       // else if (osName.startsWith("SunOS")
+                       // || osName.startsWith("Solaris"))
+                       // auth = "Solaris";
+                       // else
+                       // auth = "Unix";
+                       //
+                       // Subject subject;
+                       // // see http://old.nabble.com/osgi-and-jaas-td23485885.html
+                       // ClassLoader ccl = Thread.currentThread()
+                       // .getContextClassLoader();
+                       // try {
+                       // Thread.currentThread().setContextClassLoader(
+                       // getClass().getClassLoader());
+                       // URL url = getClass().getResource(
+                       // "/org/argeo/security/activemq/osLogin.conf");
+                       //
+                       // System.setProperty(LOGIN_CONFIG_PROPERTY, url.toString());
+                       // LoginContext lc = new LoginContext(auth);
+                       // lc.login();
+                       // subject = lc.getSubject();
+                       // } catch (LoginException le) {
+                       // throw new ArgeoException("OS authentication failed", le);
+                       // } finally {
+                       // if (oldLoginConfLocation != null)
+                       // System.setProperty(LOGIN_CONFIG_PROPERTY,
+                       // oldLoginConfLocation);
+                       // Thread.currentThread().setContextClassLoader(ccl);
+                       // }
+                       // // Extract user name
+                       // String osUsername = null;
+                       // for (Principal principal : subject.getPrincipals()) {
+                       // String className = principal.getClass().getName();
+                       // if ("Unix".equals(auth)
+                       // && "com.sun.security.auth.UnixPrincipal"
+                       // .equals(className))
+                       // osUsername = principal.getName();
+                       // else if ("Windows".equals(auth)
+                       // && "com.sun.security.auth.NTUserPrincipal"
+                       // .equals(className))
+                       // osUsername = principal.getName();
+                       // else if ("Solaris".equals(auth)
+                       // && "com.sun.security.auth.SolarisPrincipal"
+                       // .equals(className))
+                       // osUsername = principal.getName();
+                       // }
+                       //
+                       // if (osUsername == null)
+                       // throw new ArgeoException("Could not find OS user name");
+                       // }
+
+                       uccfa.setUsername(System.getProperty("user.name"));
+                       uccfa.setPassword(null);
+
+               } else if (AUTHMODE_UI.equals(authenticationMode)) {
+                       try {
+                               UIManager.setLookAndFeel(new MetalLookAndFeel());
+                       } catch (UnsupportedLookAndFeelException e) {
+                               throw new ArgeoException("Cannot load look and feel", e);
+                       }
+                       UIManager.put("ClassLoader", getClass().getClassLoader());
+                       UserPasswordDialog dialog = new UserPasswordDialog() {
+                               private static final long serialVersionUID = -891646559691412088L;
+
+                               protected void useCredentials(String username, char[] password) {
+                                       uccfa.setUsername(username);
+                                       uccfa.setPassword(new String(password));
+                               }
+                       };
+                       dialog.setVisible(true);
+               } else {
+                       throw new ArgeoException("Authentication mode '"
+                                       + authenticationMode + "' is not supported");
+               }
+
+       }
+
+       protected void prepareActiveMqSslConnectionFactory(
+                       ActiveMQSslConnectionFactory connectionFactory) {
+               try {
+                       KeyStore keyStoreKs = KeyStore.getInstance(keyStoreType);
+
+                       InputStream keyInput = keyStore.getInputStream();
+                       keyStoreKs.load(keyInput,
+                                       keyStorePassword != null ? keyStorePassword.toCharArray()
+                                                       : null);
+                       keyInput.close();
+
+                       TrustManagerFactory tmf = TrustManagerFactory
+                                       .getInstance(TrustManagerFactory.getDefaultAlgorithm());
+                       tmf.init(keyStoreKs);
+
+                       KeyManagerFactory keyManagerFactory = KeyManagerFactory
+                                       .getInstance(KeyManagerFactory.getDefaultAlgorithm());
+                       keyManagerFactory.init(keyStoreKs, keyStorePassword.toCharArray());
+
+                       connectionFactory.setKeyAndTrustManagers(
+                                       keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(),
+                                       new SecureRandom());
+               } catch (Exception e) {
+                       throw new ArgeoException(
+                                       "Cannot initialize JMS connection factory", e);
+               }
+
+       }
+
+       public void destroy() throws Exception {
+               if (cachingConnectionFactory != null)
+                       cachingConnectionFactory.destroy();
+       }
+
+       public void setKeyStorePassword(String keyStorePassword) {
+               this.keyStorePassword = keyStorePassword;
+       }
+
+       public void setKeyStore(Resource keyStore) {
+               this.keyStore = keyStore;
+       }
+
+       public void setKeyStoreType(String keyStoreType) {
+               this.keyStoreType = keyStoreType;
+       }
+
+       public void setBrokerURL(String brokerUrl) {
+               this.brokerURL = brokerUrl;
+       }
+
+       public void setAuthenticationMode(String authenticationMode) {
+               this.authenticationMode = authenticationMode;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.activemq/src/main/java/org/argeo/security/activemq/UserPasswordDialog.java b/trunk/security/runtime/org.argeo.security.activemq/src/main/java/org/argeo/security/activemq/UserPasswordDialog.java
new file mode 100644 (file)
index 0000000..747ccb1
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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.activemq;
+
+import java.awt.Container;
+import java.awt.GridLayout;
+import java.awt.Panel;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Arrays;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+
+/**
+ * Small Swing-based UI to pass user/name and password. TODO better integrate
+ * with JAAS callbacks.
+ */
+public class UserPasswordDialog extends JDialog implements ActionListener {
+       private static final long serialVersionUID = -9052993072210981198L;
+       private static String OK = "ok";
+
+       private JTextField username = new JTextField("", 10);
+       private JPasswordField password = new JPasswordField("", 10);
+
+       private JButton okButton;
+       private JButton cancelButton;
+
+       public UserPasswordDialog() {
+               setTitle("Credentials");
+               setModal(true);
+               setLocationRelativeTo(null);
+               setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+
+               JPanel p1 = new JPanel(new GridLayout(2, 2, 3, 3));
+               p1.add(new JLabel("User"));
+               p1.add(username);
+               p1.add(new JLabel("Password"));
+               password.setActionCommand(OK);
+               password.addActionListener(this);
+               p1.add(password);
+               add("Center", p1);
+
+               Panel p2 = new Panel();
+               okButton = addButton(p2, "OK");
+               okButton.setActionCommand(OK);
+               cancelButton = addButton(p2, "Cancel");
+               add("South", p2);
+               setSize(240, 120);
+
+               pack();
+       }
+
+       /** To be overridden */
+       protected void useCredentials(String username, char[] password) {
+               // does nothing
+       }
+
+       private JButton addButton(Container c, String name) {
+               JButton button = new JButton(name);
+               button.addActionListener(this);
+               c.add(button);
+               return button;
+       }
+
+       public final void actionPerformed(ActionEvent evt) {
+               Object source = evt.getSource();
+               if (source == okButton || evt.getActionCommand().equals(OK)) {
+                       char[] p = password.getPassword();
+                       useCredentials(username.getText(), p);
+                       Arrays.fill(p, '0');
+                       cleanUp();
+               } else if (source == cancelButton)
+                       cleanUp();
+       }
+
+       private void cleanUp() {
+               password.setText("");
+               dispose();
+       }
+
+       public static void main(String[] args) {
+               UserPasswordDialog dialog = new UserPasswordDialog() {
+                       private static final long serialVersionUID = -891646559691412088L;
+
+                       protected void useCredentials(String username, char[] password) {
+                               System.out.println(username + "/" + new String(password));
+                       }
+               };
+               dialog.setVisible(true);
+               System.out.println("After show");
+       }
+}
diff --git a/trunk/security/runtime/org.argeo.security.activemq/src/main/resources/org/argeo/security/activemq/osLogin.conf b/trunk/security/runtime/org.argeo.security.activemq/src/main/resources/org/argeo/security/activemq/osLogin.conf
new file mode 100644 (file)
index 0000000..17df63c
--- /dev/null
@@ -0,0 +1,12 @@
+Unix {
+  com.sun.security.auth.module.UnixLoginModule required;
+};
+
+Solaris {
+  com.sun.security.auth.module.SolarisLoginModule required;
+};
+
+Windows {
+  com.sun.security.auth.module.NTLoginModule required;
+};
+
diff --git a/trunk/security/runtime/org.argeo.security.core/.classpath b/trunk/security/runtime/org.argeo.security.core/.classpath
new file mode 100644 (file)
index 0000000..b639b53
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+       <classpathentry kind="src" output="target/classes" path="src/main/resources"/>
+       <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+       <classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/trunk/security/runtime/org.argeo.security.core/.project b/trunk/security/runtime/org.argeo.security.core/.project
new file mode 100644 (file)
index 0000000..a8e385a
--- /dev/null
@@ -0,0 +1,28 @@
+<?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/trunk/security/runtime/org.argeo.security.core/META-INF/spring/logger.xml b/trunk/security/runtime/org.argeo.security.core/META-INF/spring/logger.xml
new file mode 100644 (file)
index 0000000..02f48c8
--- /dev/null
@@ -0,0 +1,36 @@
+<?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-2.5.xsd">
+
+       <!-- Log4j appender singleton -->
+       <bean id="secureLogger" class="org.argeo.security.log4j.SecureLogger"
+               init-method="init" destroy-method="destroy">
+<!--           <property name="configuration"> -->
+<!--                   <value><![CDATA[ -->
+<!-- log4j.rootLogger=WARN, console -->
+
+<!-- ## Levels -->
+<!-- log4j.logger.org.argeo=DEBUG -->
+<!-- log4j.logger.org.argeo.jackrabbit.remote.ExtendedDispatcherServlet=ERROR -->
+<!-- log4j.logger.org.springframework.web.servlet.PageNotFound=ERROR -->
+<!-- log4j.logger.org.argeo.server.webextender.TomcatDeployer=WARN -->
+
+<!-- log4j.logger.org.apache.coyote=INFO -->
+<!-- log4j.logger.org.apache.catalina.core.ContainerBase=INFO -->
+<!-- log4j.logger.org.apache.directory.server=ERROR -->
+<!-- log4j.logger.org.apache.jackrabbit.core.query.lucene=ERROR -->
+<!-- log4j.logger.org.apache.jackrabbit.core.config.ConfigurationErrorHandler=ERROR -->
+<!-- log4j.logger.org.apache.jackrabbit.core.util.db.DbUtility=FATAL -->
+<!-- log4j.logger.org.apache.activemq=INFO -->
+<!-- log4j.logger.org.apache.activemq.ActiveMQMessageConsumer=INFO -->
+<!-- log4j.logger.org.apache.activemq.ActiveMQMessageProducer=INFO -->
+
+<!-- log4j.appender.console=org.apache.log4j.ConsoleAppender -->
+<!-- log4j.appender.console.layout=org.apache.log4j.PatternLayout -->
+<!-- log4j.appender.console.layout.ConversionPattern=%d{yyMMdd HH:mm:ss} %-5p %m [%t] %c%n -->
+<!--                   ]]></value> -->
+<!--           </property> -->
+       </bean>
+</beans>
diff --git a/trunk/security/runtime/org.argeo.security.core/META-INF/spring/osgi.xml b/trunk/security/runtime/org.argeo.security.core/META-INF/spring/osgi.xml
new file mode 100644 (file)
index 0000000..5e1433c
--- /dev/null
@@ -0,0 +1,12 @@
+<?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
+\r
+       <!-- SERVICES -->\r
+       <service ref="secureLogger" interface="org.argeo.ArgeoLogger" />\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/security/runtime/org.argeo.security.core/build.properties b/trunk/security/runtime/org.argeo.security.core/build.properties
new file mode 100644 (file)
index 0000000..788870d
--- /dev/null
@@ -0,0 +1,13 @@
+additional.bundles = org.springframework.transaction,\
+                     org.springframework.core,\
+                     junit,\
+                     org.apache.commons.io,\
+                     org.apache.commons.codec,\
+                     org.springframework.security.core,\
+                     org.apache.log4j,\
+                     slf4j.api,\
+                     slf4j.org.apache.commons.logging
+source.. = src/main/java/,\
+           src/main/resources/,\
+           src/test/java/,\
+           src/test/resources/
diff --git a/trunk/security/runtime/org.argeo.security.core/pom.xml b/trunk/security/runtime/org.argeo.security.core/pom.xml
new file mode 100644 (file)
index 0000000..1b5aa60
--- /dev/null
@@ -0,0 +1,138 @@
+<?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.security</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.core</artifactId>
+       <name>Commons Security Core</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-ActivationPolicy>lazy</Bundle-ActivationPolicy>
+                                               <Export-Package>
+                                                       org.argeo.security.*
+                                               </Export-Package>
+                                               <!-- We need to exclude some packages which are added by BND but cause 
+                                                       use package conflict with some deployments -->
+                                               <Import-Package>
+                                                       org.bouncycastle.*;resolution:=optional,
+                                                       javax.jcr.security,
+                                                       *,
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.jcr</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Crypto -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>bcprov</artifactId>
+                       <optional>true</optional>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.codec</artifactId>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.core</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.beans</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.context</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.security.core</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.transaction</artifactId>
+               </dependency>
+
+               <!-- OSGi -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.eclipse.osgi</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.log4j</artifactId>
+                       <optional>true</optional>
+               </dependency>
+
+               <!-- TEST -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>junit</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.log4j</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.json</artifactId>
+                       <version>2.1.11</version>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>com.springsource.json</artifactId>
+                       <scope>test</scope>
+               </dependency>
+
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/NodeAuthenticationToken.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/NodeAuthenticationToken.java
new file mode 100644 (file)
index 0000000..1870675
--- /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;
+
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+
+/** Credentials required for the authentication to a node. */
+public class NodeAuthenticationToken extends
+               UsernamePasswordAuthenticationToken {
+       private static final long serialVersionUID = 1955222132884795213L;
+       private final String url;
+
+       /** Non authenticated local constructor */
+       public NodeAuthenticationToken(Object principal, Object credentials) {
+               super(principal, credentials);
+               this.url = null;
+       }
+
+       /** Non authenticated remote constructor */
+       public NodeAuthenticationToken(Object principal, Object credentials,
+                       String url) {
+               super(principal, credentials);
+               this.url = url;
+       }
+
+       /** Authenticated constructor */
+       public NodeAuthenticationToken(NodeAuthenticationToken sat,
+                       GrantedAuthority[] authorities) {
+               super(sat.getPrincipal(), sat.getCredentials(), authorities);
+               this.url = sat.getUrl();
+       }
+
+       public String getUrl() {
+               return url;
+       }
+
+       public Boolean isRemote() {
+               return url != null;
+       }
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/OsAuthenticationToken.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/OsAuthenticationToken.java
new file mode 100644 (file)
index 0000000..b3727b2
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * 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;
+
+import java.security.AccessController;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+
+import org.argeo.ArgeoException;
+import org.argeo.OperatingSystem;
+import org.springframework.security.Authentication;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.userdetails.UserDetails;
+
+/** Abstracts principals provided by com.sun.security.auth.module login modules. */
+public class OsAuthenticationToken implements Authentication {
+       private static final long serialVersionUID = -7544626794250917244L;
+
+       final Class<? extends Principal> osUserPrincipalClass;
+       final Class<? extends Principal> osUserIdPrincipalClass;
+       final Class<? extends Principal> osGroupIdPrincipalClass;
+
+       private List<GrantedAuthority> grantedAuthorities;
+
+       private UserDetails details;
+
+       /** Request */
+       public OsAuthenticationToken(GrantedAuthority[] grantedAuthorities) {
+               this.grantedAuthorities = grantedAuthorities != null ? Arrays
+                               .asList(grantedAuthorities) : null;
+               ClassLoader cl = getClass().getClassLoader();
+               switch (OperatingSystem.os) {
+               case OperatingSystem.WINDOWS:
+                       osUserPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.NTUserPrincipal");
+                       osUserIdPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.NTSidUserPrincipal");
+                       osGroupIdPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.NTSidGroupPrincipal");
+                       break;
+               case OperatingSystem.NIX:
+                       osUserPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.UnixPrincipal");
+                       osUserIdPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.UnixNumericUserPrincipal");
+                       osGroupIdPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.UnixNumericGroupPrincipal");
+                       break;
+               case OperatingSystem.SOLARIS:
+                       osUserPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.SolarisPrincipal");
+                       osUserIdPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.SolarisNumericUserPrincipal");
+                       osGroupIdPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.SolarisNumericGroupPrincipal");
+                       break;
+
+               default:
+                       throw new ArgeoException("Unsupported operating system "
+                                       + OperatingSystem.os);
+               }
+
+       }
+
+       /** Authenticated */
+       public OsAuthenticationToken() {
+               this(null);
+       }
+
+       /** @return the name, or null if not yet logged */
+       public String getName() {
+               Subject subject = Subject.getSubject(AccessController.getContext());
+               if (subject == null)
+                       return null;
+               return getUser().getName();
+       }
+
+       /**
+        * Should not be called during authentication since group IDs are not yet
+        * available {@link Subject} has been set
+        */
+       public GrantedAuthority[] getAuthorities() {
+               // grantedAuthorities should not be null at this stage
+               List<GrantedAuthority> gas = new ArrayList<GrantedAuthority>(
+                               grantedAuthorities);
+               for (Principal groupPrincipal : getGroupsIds()) {
+                       gas.add(new GrantedAuthorityImpl("OSGROUP_"
+                                       + groupPrincipal.getName()));
+               }
+               return gas.toArray(new GrantedAuthority[gas.size()]);
+       }
+
+       public UserDetails getDetails() {
+               return details;
+       }
+
+       public void setDetails(UserDetails details) {
+               this.details = details;
+       }
+
+       public boolean isAuthenticated() {
+               return grantedAuthorities != null;
+       }
+
+       public void setAuthenticated(boolean isAuthenticated)
+                       throws IllegalArgumentException {
+               if (grantedAuthorities != null)
+                       grantedAuthorities.clear();
+               grantedAuthorities = null;
+       }
+
+       @SuppressWarnings("unchecked")
+       protected static Class<? extends Principal> getPrincipalClass(
+                       ClassLoader cl, String className) {
+               try {
+                       return (Class<? extends Principal>) cl.loadClass(className);
+               } catch (ClassNotFoundException e) {
+                       throw new ArgeoException("Cannot load principal class", e);
+               }
+       }
+
+       public Object getPrincipal() {
+               return getUser();
+       }
+
+       public Principal getUser() {
+               Subject subject = getSubject();
+               Set<? extends Principal> userPrincipals = subject
+                               .getPrincipals(osUserPrincipalClass);
+               if (userPrincipals == null || userPrincipals.size() == 0)
+                       throw new ArgeoException("No OS principal");
+               if (userPrincipals.size() > 1)
+                       throw new ArgeoException("More than one OS principal");
+               Principal user = userPrincipals.iterator().next();
+               return user;
+       }
+
+       public Principal getUserId() {
+               Subject subject = getSubject();
+               Set<? extends Principal> userIdsPrincipals = subject
+                               .getPrincipals(osUserIdPrincipalClass);
+               if (userIdsPrincipals == null || userIdsPrincipals.size() == 0)
+                       throw new ArgeoException("No user id principal");
+               if (userIdsPrincipals.size() > 1)
+                       throw new ArgeoException("More than one user id principal");
+               Principal userId = userIdsPrincipals.iterator().next();
+               return userId;
+       }
+
+       public Set<? extends Principal> getGroupsIds() {
+               Subject subject = getSubject();
+               return (Set<? extends Principal>) subject
+                               .getPrincipals(osGroupIdPrincipalClass);
+       }
+
+       /** @return the subject always non null */
+       protected Subject getSubject() {
+               Subject subject = Subject.getSubject(AccessController.getContext());
+               if (subject == null)
+                       throw new ArgeoException("No subject in JAAS context");
+               return subject;
+       }
+
+       public Object getCredentials() {
+               return "";
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SecurityUtils.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SecurityUtils.java
new file mode 100644 (file)
index 0000000..e5b8ae7
--- /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.security;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.springframework.security.Authentication;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
+
+/** Static utilities */
+public class SecurityUtils {
+
+       private SecurityUtils() {
+       }
+
+       /** Whether the current thread has the admin role */
+       public static boolean hasCurrentThreadAuthority(String authority) {
+               SecurityContext securityContext = SecurityContextHolder.getContext();
+               if (securityContext != null) {
+                       Authentication authentication = securityContext.getAuthentication();
+                       if (authentication != null) {
+                               for (GrantedAuthority ga : authentication.getAuthorities())
+                                       if (ga.getAuthority().equals(authority))
+                                               return true;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * @return the authenticated username or null if not authenticated /
+        *         anonymous
+        */
+       public static String getCurrentThreadUsername() {
+               SecurityContext securityContext = SecurityContextHolder.getContext();
+               if (securityContext != null) {
+                       Authentication authentication = securityContext.getAuthentication();
+                       if (authentication != null) {
+                               if (authentication instanceof AnonymousAuthenticationToken) {
+                                       return null;
+                               }
+                               return authentication.getName();
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Returns the display name of the user details (by calling toString() on
+        * it)
+        */
+       public static String getUserDetailsDisplayName() {
+               SecurityContext securityContext = SecurityContextHolder.getContext();
+               if (securityContext != null) {
+                       Authentication authentication = securityContext.getAuthentication();
+                       if (authentication != null) {
+                               if (authentication instanceof AnonymousAuthenticationToken) {
+                                       return null;
+                               }
+                               Object details = authentication.getDetails();
+                               if (details != null)
+                                       return details.toString();
+                               return authentication.getName();
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Converts an array of Spring Security {@link GrantedAuthority} to a
+        * read-only list of strings, for portability and integration
+        */
+       public static List<String> authoritiesToStringList(
+                       GrantedAuthority[] authorities) {
+               List<String> lst = new ArrayList<String>();
+               for (GrantedAuthority ga : authorities)
+                       lst.add(ga.getAuthority());
+               return Collections.unmodifiableList(lst);
+       }
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SystemAuthentication.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SystemAuthentication.java
new file mode 100644 (file)
index 0000000..2722c1f
--- /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.security;
+
+/**
+ * Marks a system authentication, that is which did not require a login process.
+ */
+public interface SystemAuthentication {
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SystemExecutionService.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SystemExecutionService.java
new file mode 100644 (file)
index 0000000..075a6c3
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
+
+/**
+ * Allows to execute code authenticated as a system user (that is not a real
+ * person). The {@link Executor} interface is not used directly in order to
+ * allow future extension of this interface and to simplify its publication
+ * (e.g. as an OSGi service) and interception.
+ */
+public interface SystemExecutionService extends Executor {
+       /**
+        * Executes this {@link Runnable} within a system authenticated context.
+        * Implementations should make sure that this method is properly secured via
+        * Java permissions since it could access everything without credentials.
+        */
+       public void execute(Runnable runnable);
+
+       /**
+        * Executes this {@link Callable} within a system authenticated context.
+        * Implementations should make sure that this method is properly secured via
+        * Java permissions since it could access everything without credentials.
+        */
+       public <T> Future<T> submit(Callable<T> task);
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/UserAdminService.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/UserAdminService.java
new file mode 100644 (file)
index 0000000..0a84cf6
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+import java.util.Set;
+
+import org.springframework.security.userdetails.UserDetailsManager;
+
+/** Enrich {@link UserDetailsManager} in order to provide roles semantics. */
+public interface UserAdminService extends UserDetailsManager {
+       /**
+        * Usernames must match this regexp pattern ({@value #USERNAME_PATTERN}).
+        * Thanks to <a href=
+        * "http://www.mkyong.com/regular-expressions/how-to-validate-username-with-regular-expression/"
+        * >this tip</a> (modified to add upper-case, add '@')
+        */
+       //public final static String USERNAME_PATTERN = "^[a-zA-Z0-9_-@]{3,64}$";
+
+       /**
+        * 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,})$";
+
+       /*
+        * USERS
+        */
+       /** List all users. */
+       public Set<String> listUsers();
+
+       /** List users having this role (except the super user). */
+       public Set<String> listUsersInRole(String role);
+
+       /** Synchronize with the underlying DAO. */
+       public void synchronize();
+
+       /*
+        * ROLES
+        */
+       public void newRole(String role);
+
+       public Set<String> listEditableRoles();
+
+       public void deleteRole(String role);
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/AbstractSystemExecution.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/AbstractSystemExecution.java
new file mode 100644 (file)
index 0000000..b84f3de
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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.core;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.security.SystemAuthentication;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationManager;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
+
+/** Provides base method for executing code with system authorization. */
+public abstract class AbstractSystemExecution {
+       static {
+               // Forces Spring Security to use inheritable strategy
+               // FIXME find a better place for forcing spring security mode
+               // doesn't work for the time being
+//             if (System.getProperty(SecurityContextHolder.SYSTEM_PROPERTY) == null)
+//                     SecurityContextHolder
+//                                     .setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
+       }
+
+       private final static Log log = LogFactory
+                       .getLog(AbstractSystemExecution.class);
+       private AuthenticationManager authenticationManager;
+       private String systemAuthenticationKey;
+
+       /** Whether the current thread was authenticated by this component. */
+       private ThreadLocal<Boolean> authenticatedBySelf = new ThreadLocal<Boolean>() {
+               protected Boolean initialValue() {
+                       return false;
+               }
+       };
+
+       /**
+        * Authenticate the calling thread to the underlying
+        * {@link AuthenticationManager}
+        */
+       protected void authenticateAsSystem() {
+               if (authenticatedBySelf.get())
+                       return;
+               SecurityContext securityContext = SecurityContextHolder.getContext();
+               Authentication currentAuth = securityContext.getAuthentication();
+               if (currentAuth != null) {
+                       if (!(currentAuth instanceof SystemAuthentication))
+                               throw new ArgeoException(
+                                               "System execution on an already authenticated thread: "
+                                                               + currentAuth + ", THREAD="
+                                                               + Thread.currentThread().getId());
+                       return;
+               }
+               // Subject subject = Subject.getSubject(AccessController.getContext());
+               // if (subject != null
+               // && !subject.getPrincipals(Authentication.class).isEmpty())
+               // throw new ArgeoException(
+               // "There is already an authenticated subject: " + subject);
+
+               String key = systemAuthenticationKey != null ? systemAuthenticationKey
+                               : System.getProperty(
+                                               InternalAuthentication.SYSTEM_KEY_PROPERTY,
+                                               InternalAuthentication.SYSTEM_KEY_DEFAULT);
+               if (key == null)
+                       throw new ArgeoException("No system key defined");
+               Authentication auth = authenticationManager
+                               .authenticate(new InternalAuthentication(key));
+               securityContext.setAuthentication(auth);
+               authenticatedBySelf.set(true);
+               if (log.isTraceEnabled())
+                       log.trace("System authenticated");
+       }
+
+       // /** Removes the authentication from the calling thread. */
+       // protected void deauthenticateAsSystem() {
+       // // remove the authentication
+       // // SecurityContext securityContext = SecurityContextHolder.getContext();
+       // // securityContext.setAuthentication(null);
+       // // authenticatedBySelf.set(false);
+       // if (log.isTraceEnabled()) {
+       // log.trace("System deauthenticated");
+       // // Thread.dumpStack();
+       // }
+       // }
+
+       /**
+        * Whether the current thread was authenticated by this component or a
+        * parent thread.
+        */
+       protected Boolean isAuthenticatedBySelf() {
+               return authenticatedBySelf.get();
+       }
+
+       public void setAuthenticationManager(
+                       AuthenticationManager authenticationManager) {
+               this.authenticationManager = authenticationManager;
+       }
+
+       public void setSystemAuthenticationKey(String systemAuthenticationKey) {
+               this.systemAuthenticationKey = systemAuthenticationKey;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/AsyncSystemTaskExecutor.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/AsyncSystemTaskExecutor.java
new file mode 100644 (file)
index 0000000..0e400c8
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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.core;
+
+import org.argeo.security.SystemExecutionService;
+import org.springframework.core.task.SimpleAsyncTaskExecutor;
+
+/**
+ * Asynchronous Spring TaskExecutor (for use in JMS for example) wrapping a
+ * {@link SystemExecutionService}.
+ */
+public class AsyncSystemTaskExecutor extends SimpleAsyncTaskExecutor {
+       private static final long serialVersionUID = -8035527542087963068L;
+
+       private SystemExecutionService systemExecutionService;
+
+       public AsyncSystemTaskExecutor() {
+               super();
+       }
+
+       public AsyncSystemTaskExecutor(String threadNamePrefix) {
+               super(threadNamePrefix);
+       }
+
+       @Override
+       public Thread createThread(final Runnable runnable) {
+               Runnable systemExecutionRunnable = new Runnable() {
+
+                       public void run() {
+                               systemExecutionService.execute(runnable);
+
+                       }
+               };
+               return super.createThread(systemExecutionRunnable);
+       }
+
+       public void setSystemExecutionService(
+                       SystemExecutionService systemExecutionService) {
+               this.systemExecutionService = systemExecutionService;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java
new file mode 100644 (file)
index 0000000..97dd6ca
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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.core;
+
+import java.beans.PropertyDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+
+/**
+ * Executes with a system authentication the instantiation and initialization
+ * methods of the application context where it has been defined.
+ */
+public class AuthenticatedApplicationContextInitialization extends
+               AbstractSystemExecution implements InstantiationAwareBeanPostProcessor,
+               ApplicationListener {
+       // private Log log = LogFactory
+       // .getLog(AuthenticatedApplicationContextInitialization.class);
+       /** If non empty, restricts to these beans */
+       private List<String> beanNames = new ArrayList<String>();
+
+       @SuppressWarnings("rawtypes")
+       public Object postProcessBeforeInstantiation(Class beanClass,
+                       String beanName) throws BeansException {
+               // we authenticate when any bean is instantiated
+               // we will deauthenticate only when the application context has been
+               // refreshed in order to be able to deal with factory beans has well
+               if (!isAuthenticatedBySelf()) {
+                       if (beanNames.size() == 0)
+                               authenticateAsSystem();
+                       else if (beanNames.contains(beanName))
+                               authenticateAsSystem();
+               }
+               return null;
+       }
+
+       public boolean postProcessAfterInstantiation(Object bean, String beanName)
+                       throws BeansException {
+               return true;
+       }
+
+       public PropertyValues postProcessPropertyValues(PropertyValues pvs,
+                       PropertyDescriptor[] pds, Object bean, String beanName)
+                       throws BeansException {
+               return pvs;
+       }
+
+       public Object postProcessBeforeInitialization(Object bean, String beanName)
+                       throws BeansException {
+               // authenticateAsSystem();
+               return bean;
+       }
+
+       public Object postProcessAfterInitialization(Object bean, String beanName)
+                       throws BeansException {
+               // NOTE: in case there was an exception in on the initialization method
+               // we expect the underlying thread to die and thus the system
+               // authentication to be lost. We have currently no way to catch the
+               // exception and perform the deauthentication by ourselves.
+               // deauthenticateAsSystem();
+               return bean;
+       }
+
+       public void onApplicationEvent(ApplicationEvent event) {
+               if (event instanceof ContextRefreshedEvent) {
+                       // make sure that we have deauthenticated after the application
+                       // context was initialized/refreshed
+                       // deauthenticateAsSystem();
+               }
+       }
+
+       public void setBeanNames(List<String> beanNames) {
+               this.beanNames = beanNames;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/AuthenticationProvidersRegister.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/AuthenticationProvidersRegister.java
new file mode 100644 (file)
index 0000000..317815e
--- /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.security.core;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * Maintains a list of authentication providers injected in to a provider
+ * manager, in order to avoid issues with OSGi services and use packages.
+ */
+public class AuthenticationProvidersRegister implements InitializingBean {
+       private Log log = LogFactory.getLog(AuthenticationProvidersRegister.class);
+
+       private List<Object> providers = new ArrayList<Object>();
+       private List<Object> defaultProviders = new ArrayList<Object>();
+
+       public void register(Object authenticationProvider,
+                       Map<String, String> parameters) {
+               providers.add(authenticationProvider);
+               if (log.isTraceEnabled())
+                       log.trace("Registered authentication provider " + parameters);
+       }
+
+       public void unregister(Object authenticationProvider,
+                       Map<String, String> parameters) {
+               providers.remove(authenticationProvider);
+               if (log.isTraceEnabled())
+                       log.trace("Unregistered authentication provider " + parameters);
+       }
+
+       public List<Object> getProviders() {
+               return providers;
+       }
+
+       public void setDefaultProviders(
+                       List<Object> defaultProviders) {
+               this.defaultProviders = defaultProviders;
+       }
+
+       public void afterPropertiesSet() throws Exception {
+               providers.addAll(defaultProviders);
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/ConsoleCallbackHandler.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/ConsoleCallbackHandler.java
new file mode 100644 (file)
index 0000000..faa81b0
--- /dev/null
@@ -0,0 +1,70 @@
+package org.argeo.security.core;
+
+import java.io.Console;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Locale;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.TextOutputCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.argeo.ArgeoException;
+import org.argeo.util.LocaleCallback;
+
+/** Callback handler to be used with a command line UI. */
+public class ConsoleCallbackHandler implements CallbackHandler {
+
+       @Override
+       public void handle(Callback[] callbacks) throws IOException,
+                       UnsupportedCallbackException {
+               Console console = System.console();
+               if (console == null)
+                       throw new ArgeoException("No console available");
+
+               PrintWriter writer = console.writer();
+               for (int i = 0; i < callbacks.length; i++) {
+                       if (callbacks[i] instanceof TextOutputCallback) {
+                               TextOutputCallback callback = (TextOutputCallback) callbacks[i];
+                               writer.write(callback.getMessage());
+                       } else if (callbacks[i] instanceof NameCallback) {
+                               NameCallback callback = (NameCallback) callbacks[i];
+                               writer.write(callback.getPrompt());
+                               if (callback.getDefaultName() != null)
+                                       writer.write(" (" + callback.getDefaultName() + ")");
+                               writer.write(" : ");
+                               String answer = console.readLine();
+                               if (callback.getDefaultName() != null
+                                               && answer.trim().equals(""))
+                                       callback.setName(callback.getDefaultName());
+                               else
+                                       callback.setName(answer);
+                       } else if (callbacks[i] instanceof PasswordCallback) {
+                               PasswordCallback callback = (PasswordCallback) callbacks[i];
+                               writer.write(callback.getPrompt());
+                               char[] answer = console.readPassword();
+                               callback.setPassword(answer);
+                               Arrays.fill(answer, ' ');
+                       } else if (callbacks[i] instanceof LocaleCallback) {
+                               LocaleCallback callback = (LocaleCallback) callbacks[i];
+                               writer.write(callback.getPrompt());
+                               writer.write("\n");
+                               for (int j = 0; j < callback.getAvailableLocales().size(); j++) {
+                                       Locale locale = callback.getAvailableLocales().get(j);
+                                       writer.print(j + " : " + locale.getDisplayName() + "\n");
+                               }
+                               writer.write("(" + callback.getDefaultIndex() + ") : ");
+                               String answer = console.readLine();
+                               if (answer.trim().equals(""))
+                                       callback.setSelectedIndex(callback.getDefaultIndex());
+                               else
+                                       callback.setSelectedIndex(new Integer(answer.trim()));
+                       }
+               }
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/InternalAuthentication.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/InternalAuthentication.java
new file mode 100644 (file)
index 0000000..267ddd3
--- /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.core;
+
+import org.argeo.security.SystemAuthentication;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.adapters.PrincipalSpringSecurityUserToken;
+
+/** A token base on a system key used to request a system authentication. */
+public class InternalAuthentication extends PrincipalSpringSecurityUserToken
+               implements SystemAuthentication {
+       private static final long serialVersionUID = -6783376375615949315L;
+       /** 'admin' for consistency with JCR */
+       public final static String DEFAULT_SYSTEM_USERNAME = "admin";
+       public final static String DEFAULT_SYSTEM_ROLE = "ROLE_SYSTEM";
+       public final static String SYSTEM_KEY_PROPERTY = "argeo.security.systemKey";
+       public final static String SYSTEM_KEY_DEFAULT = "argeo";
+
+       public InternalAuthentication(String key, String systemUsername,
+                       String systemRole) {
+               super(
+                               key,
+                               systemUsername,
+                               key,
+                               new GrantedAuthority[] { new GrantedAuthorityImpl(systemRole) },
+                               systemUsername);
+       }
+
+       public InternalAuthentication(String key) {
+               this(key, DEFAULT_SYSTEM_USERNAME, DEFAULT_SYSTEM_ROLE);
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/KeyBasedSystemExecutionService.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/KeyBasedSystemExecutionService.java
new file mode 100644 (file)
index 0000000..6c85df1
--- /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.core;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+
+import org.argeo.ArgeoException;
+import org.argeo.security.SystemExecutionService;
+
+/**
+ * Implementation of a {@link SystemExecutionService} using a key-based
+ * {@link InternalAuthentication}
+ */
+public class KeyBasedSystemExecutionService extends AbstractSystemExecution
+               implements SystemExecutionService {
+       public void execute(Runnable runnable) {
+               try {
+                       wrapWithSystemAuthentication(Executors.callable(runnable)).call();
+               } catch (RuntimeException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new ArgeoException(
+                                       "Exception when running system authenticated task", e);
+               }
+       }
+
+       public <T> Future<T> submit(Callable<T> task) {
+               FutureTask<T> future = new FutureTask<T>(
+                               wrapWithSystemAuthentication(task));
+               future.run();
+               return future;
+       }
+
+       protected <T> Callable<T> wrapWithSystemAuthentication(
+                       final Callable<T> runnable) {
+               return new Callable<T>() {
+
+                       public T call() throws Exception {
+                               authenticateAsSystem();
+                               try {
+                                       return runnable.call();
+                               } finally {
+//                                     deauthenticateAsSystem();
+                               }
+                       }
+               };
+       }
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/MatchingAuthenticationProvider.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/MatchingAuthenticationProvider.java
new file mode 100644 (file)
index 0000000..0471151
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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.core;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.springframework.core.io.Resource;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+import org.springframework.security.providers.dao.AbstractUserDetailsAuthenticationProvider;
+import org.springframework.security.userdetails.User;
+import org.springframework.security.userdetails.UserDetails;
+
+/** @deprecated */
+@Deprecated
+public class MatchingAuthenticationProvider extends
+               AbstractUserDetailsAuthenticationProvider {
+
+       private Resource mapping;
+       private Properties properties;
+
+       private List<String> defaultRoles = new ArrayList<String>();
+
+       @Override
+       protected void doAfterPropertiesSet() throws Exception {
+               properties = new Properties();
+               InputStream propIn = mapping.getInputStream();
+               try {
+                       properties.load(propIn);
+               } finally {
+                       propIn.close();
+               }
+       }
+
+       @Override
+       protected void additionalAuthenticationChecks(UserDetails userDetails,
+                       UsernamePasswordAuthenticationToken authentication)
+                       throws AuthenticationException {
+               if (!userDetails.getPassword().equals(authentication.getCredentials()))
+                       throw new BadCredentialsException(
+                                       "Invalid credentails provided by "
+                                                       + authentication.getName());
+       }
+
+       @Override
+       protected UserDetails retrieveUser(String username,
+                       UsernamePasswordAuthenticationToken authentication)
+                       throws AuthenticationException {
+               String value = properties.getProperty(username);
+               if (value == null)
+                       throw new BadCredentialsException("User " + username
+                                       + " is not registered");
+               List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
+               for (String role : defaultRoles)
+                       grantedAuthorities.add(new GrantedAuthorityImpl(role));
+               return new User(
+                               username,
+                               value,
+                               true,
+                               true,
+                               true,
+                               true,
+                               grantedAuthorities
+                                               .toArray(new GrantedAuthority[grantedAuthorities.size()]));
+       }
+
+       public void setMapping(Resource mapping) {
+               this.mapping = mapping;
+       }
+
+       public void setDefaultRoles(List<String> defaultRoles) {
+               this.defaultRoles = defaultRoles;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/OsAuthenticationProvider.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/OsAuthenticationProvider.java
new file mode 100644 (file)
index 0000000..0e29ecd
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.security.OsAuthenticationToken;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.providers.AuthenticationProvider;
+
+/**
+ * Validates an OS authentication. The id is that it will always be
+ * authenticated since we are always runnign within an OS, but the fact that the
+ * {@link Authentication} works properly depends on the proper OS login module
+ * having been called as well. TODO make it more configurable (base roles, is
+ * admin)
+ */
+public class OsAuthenticationProvider implements AuthenticationProvider {
+       final static String osUserRole = "ROLE_OS_USER";
+       final static String userRole = "ROLE_USER";
+       final static String adminRole = "ROLE_ADMIN";
+
+       final static Boolean isAdmin = true;
+
+       public Authentication authenticate(Authentication authentication)
+                       throws AuthenticationException {
+               return new OsAuthenticationToken(getBaseAuthorities());
+       }
+
+       public static GrantedAuthority[] getBaseAuthorities() {
+               List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
+               auths.add(new GrantedAuthorityImpl(osUserRole));
+               auths.add(new GrantedAuthorityImpl(userRole));
+               if (isAdmin)
+                       auths.add(new GrantedAuthorityImpl(adminRole));
+               return auths.toArray(new GrantedAuthority[auths.size()]);
+       }
+
+       @SuppressWarnings("rawtypes")
+       public boolean supports(Class authentication) {
+               return OsAuthenticationToken.class.isAssignableFrom(authentication);
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/OsgiModuleLabel.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/OsgiModuleLabel.java
new file mode 100644 (file)
index 0000000..45c9e16
--- /dev/null
@@ -0,0 +1,41 @@
+package org.argeo.security.core;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+/**
+ * Logs the name and version of an OSGi bundle based on its
+ * {@link BundleContext}.
+ */
+public class OsgiModuleLabel {
+       private final static Log log = LogFactory.getLog(OsgiModuleLabel.class);
+
+       private Bundle bundle;
+
+       public OsgiModuleLabel() {
+       }
+
+       /** Sets without logging. */
+       public OsgiModuleLabel(Bundle bundle) {
+               this.bundle = bundle;
+       }
+
+       /**
+        * Retrieved bundle from a bundle context and logs it. Typically to be set
+        * as a Spring bean.
+        */
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundle = bundleContext.getBundle();
+               log.info(msg());
+       }
+
+       public String msg() {
+               String name = bundle.getHeaders().get(Constants.BUNDLE_NAME).toString();
+               String symbolicName = bundle.getSymbolicName();
+               String version = bundle.getVersion().toString();
+               return name + " v" + version + " (" + symbolicName + ")";
+       }
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/SimpleRoleRegistration.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/SimpleRoleRegistration.java
new file mode 100644 (file)
index 0000000..aa8a5f0
--- /dev/null
@@ -0,0 +1,57 @@
+package org.argeo.security.core;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.security.UserAdminService;
+
+/**
+ * Register one or many roles via a user admin service. Does nothing if the role
+ * is already registered.
+ */
+public class SimpleRoleRegistration implements Runnable {
+       private final static Log log = LogFactory
+                       .getLog(SimpleRoleRegistration.class);
+
+       private String role;
+       private List<String> roles = new ArrayList<String>();
+       private UserAdminService userAdminService;
+
+       @Override
+       public void run() {
+               Set<String> existingRoles = userAdminService.listEditableRoles();
+               if (role != null && !existingRoles.contains(role))
+                       newRole(role);
+               for (String r : roles) {
+                       if (!existingRoles.contains(r))
+                               newRole(r);
+               }
+       }
+
+       protected void newRole(String r) {
+               userAdminService.newRole(r);
+               log.info("Added role " + r + " required by application.");
+       }
+
+       public void register(UserAdminService userAdminService, Map<?, ?> properties) {
+               this.userAdminService = userAdminService;
+               run();
+       }
+
+       public void setRole(String role) {
+               this.role = role;
+       }
+
+       public void setRoles(List<String> roles) {
+               this.roles = roles;
+       }
+
+       public void setUserAdminService(UserAdminService userAdminService) {
+               this.userAdminService = userAdminService;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/AbstractKeyring.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/AbstractKeyring.java
new file mode 100644 (file)
index 0000000..daa1ebd
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * 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.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.security.AccessController;
+import java.security.MessageDigest;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import javax.crypto.SecretKey;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.TextOutputCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.ArgeoException;
+import org.argeo.StreamUtils;
+import org.argeo.util.security.Keyring;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+/** username / password based keyring. TODO internationalize */
+public abstract class AbstractKeyring implements Keyring, CryptoKeyring {
+       static {
+               Security.addProvider(new BouncyCastleProvider());
+       }
+
+       public final static String DEFAULT_KEYRING_LOGIN_CONTEXT = "KEYRING";
+
+       private String loginContextName = DEFAULT_KEYRING_LOGIN_CONTEXT;
+       private CallbackHandler defaultCallbackHandler;
+
+       private String charset = "UTF-8";
+
+       /**
+        * Default provider is bouncy castle, in order to have consistent behaviour
+        * across implementations
+        */
+       private String securityProviderName = "BC";
+
+       /**
+        * Whether the keyring has already been created in the past with a master
+        * password
+        */
+       protected abstract Boolean isSetup();
+
+       /**
+        * Setup the keyring persistently, {@link #isSetup()} must return true
+        * afterwards
+        */
+       protected abstract void setup(char[] password);
+
+       /** Populates the key spec callback */
+       protected abstract void handleKeySpecCallback(PBEKeySpecCallback pbeCallback);
+
+       protected abstract void encrypt(String path, InputStream unencrypted);
+
+       protected abstract InputStream decrypt(String path);
+
+       /** Triggers lazy initialization */
+       protected SecretKey getSecretKey() {
+               Subject subject = Subject.getSubject(AccessController.getContext());
+               // we assume only one secrete key is available
+               Iterator<SecretKey> iterator = subject.getPrivateCredentials(
+                               SecretKey.class).iterator();
+               if (!iterator.hasNext()) {// not initialized
+                       CallbackHandler callbackHandler = new KeyringCallbackHandler();
+                       try {
+                               LoginContext loginContext = new LoginContext(loginContextName,
+                                               subject, callbackHandler);
+                               loginContext.login();
+                               // FIXME will login even if password is wrong
+                               iterator = subject.getPrivateCredentials(SecretKey.class)
+                                               .iterator();
+                               return iterator.next();
+                       } catch (LoginException e) {
+                               throw new ArgeoException("Keyring login failed", e);
+                       }
+
+               } else {
+                       SecretKey secretKey = iterator.next();
+                       if (iterator.hasNext())
+                               throw new ArgeoException(
+                                               "More than one secret key in private credentials");
+                       return secretKey;
+               }
+       }
+
+       public InputStream getAsStream(String path) {
+               return decrypt(path);
+       }
+
+       public void set(String path, InputStream in) {
+               encrypt(path, in);
+       }
+
+       public char[] getAsChars(String path) {
+               InputStream in = getAsStream(path);
+               CharArrayWriter writer = null;
+               Reader reader = null;
+               try {
+                       writer = new CharArrayWriter();
+                       reader = new InputStreamReader(in, charset);
+                       StreamUtils.copy(reader, writer);
+                       return writer.toCharArray();
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot decrypt to char array", e);
+               } finally {
+                       StreamUtils.closeQuietly(reader);
+                       StreamUtils.closeQuietly(in);
+                       StreamUtils.closeQuietly(writer);
+               }
+       }
+
+       public void set(String path, char[] arr) {
+               ByteArrayOutputStream out = new ByteArrayOutputStream();
+               ByteArrayInputStream in = null;
+               Writer writer = null;
+               try {
+                       writer = new OutputStreamWriter(out, charset);
+                       writer.write(arr);
+                       writer.flush();
+                       in = new ByteArrayInputStream(out.toByteArray());
+                       set(path, in);
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot encrypt to char array", e);
+               } finally {
+                       StreamUtils.closeQuietly(writer);
+                       StreamUtils.closeQuietly(out);
+                       StreamUtils.closeQuietly(in);
+               }
+       }
+
+       protected Provider getSecurityProvider() {
+               return Security.getProvider(securityProviderName);
+       }
+
+       public void setLoginContextName(String loginContextName) {
+               this.loginContextName = loginContextName;
+       }
+
+       public void setDefaultCallbackHandler(CallbackHandler defaultCallbackHandler) {
+               this.defaultCallbackHandler = defaultCallbackHandler;
+       }
+
+       public void setCharset(String charset) {
+               this.charset = charset;
+       }
+
+       public void setSecurityProviderName(String securityProviderName) {
+               this.securityProviderName = securityProviderName;
+       }
+
+       @Deprecated
+       protected static byte[] hash(char[] password, byte[] salt,
+                       Integer iterationCount) {
+               ByteArrayOutputStream out = null;
+               OutputStreamWriter writer = null;
+               try {
+                       out = new ByteArrayOutputStream();
+                       writer = new OutputStreamWriter(out, "UTF-8");
+                       writer.write(password);
+                       MessageDigest pwDigest = MessageDigest.getInstance("SHA-256");
+                       pwDigest.reset();
+                       pwDigest.update(salt);
+                       byte[] btPass = pwDigest.digest(out.toByteArray());
+                       for (int i = 0; i < iterationCount; i++) {
+                               pwDigest.reset();
+                               btPass = pwDigest.digest(btPass);
+                       }
+                       return btPass;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot hash", e);
+               } finally {
+                       StreamUtils.closeQuietly(out);
+                       StreamUtils.closeQuietly(writer);
+               }
+
+       }
+
+       /**
+        * Convenience method using the underlying callback to ask for a password
+        * (typically used when the password is not saved in the keyring)
+        */
+       protected char[] ask() {
+               PasswordCallback passwordCb = new PasswordCallback("Password", false);
+               Callback[] dialogCbs = new Callback[] { passwordCb };
+               try {
+                       defaultCallbackHandler.handle(dialogCbs);
+                       char[] password = passwordCb.getPassword();
+                       return password;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot ask for a password", e);
+               }
+
+       }
+
+       class KeyringCallbackHandler implements CallbackHandler {
+               public void handle(Callback[] callbacks) throws IOException,
+                               UnsupportedCallbackException {
+                       // checks
+                       if (callbacks.length != 2)
+                               throw new IllegalArgumentException(
+                                               "Keyring required 2 and only 2 callbacks: {PasswordCallback,PBEKeySpecCallback}");
+                       if (!(callbacks[0] instanceof PasswordCallback))
+                               throw new UnsupportedCallbackException(callbacks[0]);
+                       if (!(callbacks[1] instanceof PBEKeySpecCallback))
+                               throw new UnsupportedCallbackException(callbacks[0]);
+
+                       PasswordCallback passwordCb = (PasswordCallback) callbacks[0];
+                       PBEKeySpecCallback pbeCb = (PBEKeySpecCallback) callbacks[1];
+
+                       if (isSetup()) {
+                               Callback[] dialogCbs = new Callback[] { passwordCb };
+                               defaultCallbackHandler.handle(dialogCbs);
+                       } else {// setup keyring
+                               TextOutputCallback textCb1 = new TextOutputCallback(
+                                               TextOutputCallback.INFORMATION,
+                                               "Enter a master password which will protect your private data");
+                               TextOutputCallback textCb2 = new TextOutputCallback(
+                                               TextOutputCallback.INFORMATION,
+                                               "(for example your credentials to third-party services)");
+                               TextOutputCallback textCb3 = new TextOutputCallback(
+                                               TextOutputCallback.INFORMATION,
+                                               "Don't forget this password since the data cannot be read without it");
+                               PasswordCallback confirmPasswordCb = new PasswordCallback(
+                                               "Confirm password", false);
+                               // first try
+                               Callback[] dialogCbs = new Callback[] { textCb1, textCb2,
+                                               textCb3, passwordCb, confirmPasswordCb };
+                               defaultCallbackHandler.handle(dialogCbs);
+
+                               // if passwords different, retry (except if cancelled)
+                               while (passwordCb.getPassword() != null
+                                               && !Arrays.equals(passwordCb.getPassword(),
+                                                               confirmPasswordCb.getPassword())) {
+                                       TextOutputCallback textCb = new TextOutputCallback(
+                                                       TextOutputCallback.ERROR,
+                                                       "The passwords do not match");
+                                       dialogCbs = new Callback[] { textCb, passwordCb,
+                                                       confirmPasswordCb };
+                                       defaultCallbackHandler.handle(dialogCbs);
+                               }
+
+                               if (passwordCb.getPassword() != null) {// not cancelled
+                                       setup(passwordCb.getPassword());
+                               }
+                       }
+
+                       if (passwordCb.getPassword() != null)
+                               handleKeySpecCallback(pbeCb);
+               }
+
+       }
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/CryptoKeyring.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/CryptoKeyring.java
new file mode 100644 (file)
index 0000000..d25eccd
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.crypto;
+
+import org.argeo.util.security.Keyring;
+
+/**
+ * Advanced keyring based on cryptography that can easily be centralized and
+ * coordinated with {@link KeyringLoginModule} (since they ar ein the same
+ * package)
+ */
+public interface CryptoKeyring extends Keyring {
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/KeyringLoginModule.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/KeyringLoginModule.java
new file mode 100644 (file)
index 0000000..34b7d40
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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.crypto;
+
+import java.security.AccessController;
+import java.util.Map;
+import java.util.Set;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+/** Adds a secret key to the private credentials */
+public class KeyringLoginModule implements LoginModule {
+       private Subject subject;
+       private CallbackHandler callbackHandler;
+       private SecretKey secretKey;
+
+       public void initialize(Subject subject, CallbackHandler callbackHandler,
+                       Map<String, ?> sharedState, Map<String, ?> options) {
+               this.subject = subject;
+               if (subject == null) {
+                       subject = Subject.getSubject(AccessController.getContext());
+               }
+               this.callbackHandler = callbackHandler;
+       }
+
+       public boolean login() throws LoginException {
+               Set<SecretKey> pbes = subject.getPrivateCredentials(SecretKey.class);
+               if (pbes.size() > 0)
+                       return true;
+               PasswordCallback pc = new PasswordCallback("Master password", false);
+               PBEKeySpecCallback pbeCb = new PBEKeySpecCallback();
+               Callback[] callbacks = { pc, pbeCb };
+               try {
+                       callbackHandler.handle(callbacks);
+                       char[] password = pc.getPassword();
+
+                       SecretKeyFactory keyFac = SecretKeyFactory.getInstance(pbeCb
+                                       .getSecretKeyFactory());
+                       PBEKeySpec keySpec;
+                       if (pbeCb.getKeyLength() != null)
+                               keySpec = new PBEKeySpec(password, pbeCb.getSalt(),
+                                               pbeCb.getIterationCount(), pbeCb.getKeyLength());
+                       else
+                               keySpec = new PBEKeySpec(password, pbeCb.getSalt(),
+                                               pbeCb.getIterationCount());
+
+                       String secKeyEncryption = pbeCb.getSecretKeyEncryption();
+                       if (secKeyEncryption != null) {
+                               SecretKey tmp = keyFac.generateSecret(keySpec);
+                               secretKey = new SecretKeySpec(tmp.getEncoded(),
+                                               secKeyEncryption);
+                       } else {
+                               secretKey = keyFac.generateSecret(keySpec);
+                       }
+               } catch (Exception e) {
+                       LoginException le = new LoginException("Cannot login keyring");
+                       le.initCause(e);
+                       throw le;
+               }
+               return true;
+       }
+
+       public boolean commit() throws LoginException {
+               if (secretKey != null)
+                       subject.getPrivateCredentials().add(secretKey);
+               return true;
+       }
+
+       public boolean abort() throws LoginException {
+               return true;
+       }
+
+       public boolean logout() throws LoginException {
+               Set<PasswordBasedEncryption> pbes = subject
+                               .getPrivateCredentials(PasswordBasedEncryption.class);
+               pbes.clear();
+               return true;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PBEKeySpecCallback.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PBEKeySpecCallback.java
new file mode 100644 (file)
index 0000000..e964366
--- /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.security.crypto;
+
+import javax.crypto.spec.PBEKeySpec;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.PasswordCallback;
+
+/**
+ * All information required to set up a {@link PBEKeySpec} bar the password
+ * itself (use a {@link PasswordCallback})
+ */
+public class PBEKeySpecCallback implements Callback {
+       private String secretKeyFactory;
+       private byte[] salt;
+       private Integer iterationCount;
+       /** Can be null for some algorithms */
+       private Integer keyLength;
+       /** Can be null, will trigger secret key encryption if not */
+       private String secretKeyEncryption;
+
+       private String encryptedPasswordHashCipher;
+       private byte[] encryptedPasswordHash;
+
+       public void set(String secretKeyFactory, byte[] salt,
+                       Integer iterationCount, Integer keyLength,
+                       String secretKeyEncryption) {
+               this.secretKeyFactory = secretKeyFactory;
+               this.salt = salt;
+               this.iterationCount = iterationCount;
+               this.keyLength = keyLength;
+               this.secretKeyEncryption = secretKeyEncryption;
+//             this.encryptedPasswordHashCipher = encryptedPasswordHashCipher;
+//             this.encryptedPasswordHash = encryptedPasswordHash;
+       }
+
+       public String getSecretKeyFactory() {
+               return secretKeyFactory;
+       }
+
+       public byte[] getSalt() {
+               return salt;
+       }
+
+       public Integer getIterationCount() {
+               return iterationCount;
+       }
+
+       public Integer getKeyLength() {
+               return keyLength;
+       }
+
+       public String getSecretKeyEncryption() {
+               return secretKeyEncryption;
+       }
+
+       public String getEncryptedPasswordHashCipher() {
+               return encryptedPasswordHashCipher;
+       }
+
+       public byte[] getEncryptedPasswordHash() {
+               return encryptedPasswordHash;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PasswordBasedEncryption.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PasswordBasedEncryption.java
new file mode 100644 (file)
index 0000000..aec25ac
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * 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.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.Security;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.StreamUtils;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+/** Simple password based encryption / decryption */
+public class PasswordBasedEncryption {
+       private final static Log log = LogFactory
+                       .getLog(PasswordBasedEncryption.class);
+
+       static {
+               Security.addProvider(new BouncyCastleProvider());
+       }
+
+       public final static Integer DEFAULT_ITERATION_COUNT = 1024;
+       /** Stronger with 256, but causes problem with Oracle JVM */
+       public final static Integer DEFAULT_SECRETE_KEY_LENGTH = 256;
+       public final static Integer DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED = 128;
+       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";
+       public final static String DEFAULT_CHARSET = "UTF-8";
+
+       private Integer iterationCount = DEFAULT_ITERATION_COUNT;
+       private Integer 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 static byte[] DEFAULT_SALT_8 = { (byte) 0xA9, (byte) 0x9B,
+                       (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
+                       (byte) 0x03 };
+       private static byte[] DEFAULT_IV_16 = { (byte) 0xA9, (byte) 0x9B,
+                       (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
+                       (byte) 0x03, (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
+                       (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03 };
+
+       private Key key;
+       private Cipher ecipher;
+       private Cipher dcipher;
+
+       /**
+        * Default provider is bouncy castle, in order to have consistent behaviour
+        * across implementations
+        */
+       private String securityProviderName = "BC";
+
+       /**
+        * This is up to the caller to clear the passed array. Neither copy of nor
+        * reference to the passed array is kept
+        */
+       public PasswordBasedEncryption(char[] password) {
+               this(password, DEFAULT_SALT_8, DEFAULT_IV_16);
+       }
+
+       /**
+        * This is up to the caller to clear the passed array. Neither copies of nor
+        * references to the passed arrays are kept
+        */
+       public PasswordBasedEncryption(char[] password, byte[] passwordSalt,
+                       byte[] initializationVector) {
+               try {
+                       initKeyAndCiphers(password, passwordSalt, initializationVector);
+               } catch (InvalidKeyException e) {
+                       Integer previousSecreteKeyLength = secreteKeyLength;
+                       secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED;
+                       log.warn("'" + e.getMessage() + "', will use " + secreteKeyLength
+                                       + " secrete key length instead of "
+                                       + previousSecreteKeyLength);
+                       try {
+                               initKeyAndCiphers(password, passwordSalt, initializationVector);
+                       } catch (Exception e1) {
+                               throw new ArgeoException(
+                                               "Cannot get secret key (with restricted length)", e1);
+                       }
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot get secret key", e);
+               }
+       }
+
+       protected void initKeyAndCiphers(char[] password, byte[] passwordSalt,
+                       byte[] initializationVector) throws GeneralSecurityException {
+               byte[] salt = new byte[8];
+               System.arraycopy(passwordSalt, 0, salt, 0, salt.length);
+               // for (int i = 0; i < password.length && i < salt.length; i++)
+               // salt[i] = (byte) password[i];
+               byte[] iv = new byte[16];
+               System.arraycopy(initializationVector, 0, iv, 0, iv.length);
+
+               SecretKeyFactory keyFac = SecretKeyFactory
+                               .getInstance(getSecretKeyFactoryName());
+               PBEKeySpec keySpec = new PBEKeySpec(password, salt,
+                               getIterationCount(), getKeyLength());
+               String secKeyEncryption = getSecretKeyEncryption();
+               if (secKeyEncryption != null) {
+                       SecretKey tmp = keyFac.generateSecret(keySpec);
+                       key = new SecretKeySpec(tmp.getEncoded(), getSecretKeyEncryption());
+               } else {
+                       key = keyFac.generateSecret(keySpec);
+               }
+               ecipher = Cipher.getInstance(getCipherName(), securityProviderName);
+               ecipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
+               dcipher = Cipher.getInstance(getCipherName());
+               dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
+       }
+
+       public void encrypt(InputStream decryptedIn, OutputStream encryptedOut)
+                       throws IOException {
+               try {
+                       CipherOutputStream out = new CipherOutputStream(encryptedOut,
+                                       ecipher);
+                       StreamUtils.copy(decryptedIn, out);
+                       StreamUtils.closeQuietly(out);
+               } catch (IOException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot encrypt", e);
+               } finally {
+                       StreamUtils.closeQuietly(decryptedIn);
+               }
+       }
+
+       public void decrypt(InputStream encryptedIn, OutputStream decryptedOut)
+                       throws IOException {
+               try {
+                       CipherInputStream decryptedIn = new CipherInputStream(encryptedIn,
+                                       dcipher);
+                       StreamUtils.copy(decryptedIn, decryptedOut);
+               } catch (IOException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot decrypt", e);
+               } finally {
+                       StreamUtils.closeQuietly(encryptedIn);
+               }
+       }
+
+       public byte[] encryptString(String str) {
+               ByteArrayOutputStream out = null;
+               ByteArrayInputStream in = null;
+               try {
+                       out = new ByteArrayOutputStream();
+                       in = new ByteArrayInputStream(str.getBytes(DEFAULT_CHARSET));
+                       encrypt(in, out);
+                       return out.toByteArray();
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot encrypt", e);
+               } finally {
+                       StreamUtils.closeQuietly(out);
+               }
+       }
+
+       /** Closes the input stream */
+       public String decryptAsString(InputStream in) {
+               ByteArrayOutputStream out = null;
+               try {
+                       out = new ByteArrayOutputStream();
+                       decrypt(in, out);
+                       return new String(out.toByteArray(), DEFAULT_CHARSET);
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot decrypt", e);
+               } finally {
+                       StreamUtils.closeQuietly(out);
+               }
+       }
+
+       protected Key getKey() {
+               return key;
+       }
+
+       protected Cipher getEcipher() {
+               return ecipher;
+       }
+
+       protected Cipher getDcipher() {
+               return dcipher;
+       }
+
+       protected Integer getIterationCount() {
+               return iterationCount;
+       }
+
+       protected Integer getKeyLength() {
+               return secreteKeyLength;
+       }
+
+       protected String getSecretKeyFactoryName() {
+               return secreteKeyFactoryName;
+       }
+
+       protected String getSecretKeyEncryption() {
+               return secreteKeyEncryption;
+       }
+
+       protected String getCipherName() {
+               return cipherName;
+       }
+
+       public void setIterationCount(Integer iterationCount) {
+               this.iterationCount = iterationCount;
+       }
+
+       public void setSecreteKeyLength(Integer 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;
+       }
+
+       public void setSecurityProviderName(String securityProviderName) {
+               this.securityProviderName = securityProviderName;
+       }
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrKeyring.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrKeyring.java
new file mode 100644 (file)
index 0000000..1b9f244
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * 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.jcr;
+
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayReader;
+import java.io.InputStream;
+import java.io.Reader;
+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.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.security.crypto.AbstractKeyring;
+import org.argeo.security.crypto.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 ArgeoException("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 ArgeoException("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);
+
+                       // 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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("Keyring not setup");
+                       Node keyring = userHome.getNode(ARGEO_KEYRING);
+                       Cipher cipher = Cipher.getInstance(keyring
+                                       .getProperty(ARGEO_CIPHER).getString(),
+                                       getSecurityProvider());
+                       return cipher;
+               } catch (Exception e) {
+                       throw new ArgeoException("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/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrSecurityModel.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrSecurityModel.java
new file mode 100644 (file)
index 0000000..e9ab89c
--- /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.jcr;
+
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.Session;
+
+/**
+ * Manages data expected by the Argeo security model, such as user home and
+ * profile.
+ */
+public interface JcrSecurityModel {
+       /**
+        * To be called before user details are loaded. Make sure than any logged in
+        * user has a home directory with full access and a profile with information
+        * about him (read access)
+        * 
+        * @return the user profile (whose parent is the user home), never null
+        */
+       public Node sync(Session session, String username, List<String> roles);
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrUserDetails.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrUserDetails.java
new file mode 100644 (file)
index 0000000..2f7b97b
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * 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.jcr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.UserJcrUtils;
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.DisabledException;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.LockedException;
+import org.springframework.security.userdetails.User;
+
+/** User details based on a user profile node. */
+public class JcrUserDetails extends User implements ArgeoNames {
+       private static final long serialVersionUID = -8142764995842559646L;
+       private final String homePath;
+       private final String securityWorkspace;
+
+       /** Human readable user name */
+       private String displayName;
+
+       protected JcrUserDetails(String securityWorkspace, String homePath,
+                       String username, String password, boolean enabled,
+                       boolean accountNonExpired, boolean credentialsNonExpired,
+                       boolean accountNonLocked, GrantedAuthority[] authorities)
+                       throws IllegalArgumentException {
+               super(username, password, enabled, accountNonExpired,
+                               credentialsNonExpired, accountNonLocked, authorities);
+               this.homePath = homePath;
+               this.securityWorkspace = securityWorkspace;
+       }
+
+       public JcrUserDetails(Node userProfile, String password,
+                       GrantedAuthority[] authorities) throws RepositoryException {
+               super(
+                               userProfile.getProperty(ARGEO_USER_ID).getString(),
+                               password,
+                               userProfile.getProperty(ARGEO_ENABLED).getBoolean(),
+                               userProfile.getProperty(ARGEO_ACCOUNT_NON_EXPIRED).getBoolean(),
+                               userProfile.getProperty(ARGEO_CREDENTIALS_NON_EXPIRED)
+                                               .getBoolean(), userProfile.getProperty(
+                                               ARGEO_ACCOUNT_NON_LOCKED).getBoolean(), authorities);
+               // human readable name
+               if (userProfile.hasProperty(Property.JCR_TITLE)) {
+                       displayName = userProfile.getProperty(Property.JCR_TITLE)
+                                       .getString();
+                       if (displayName.trim().equals(""))
+                               displayName = null;
+               }
+               if (displayName == null)
+                       displayName = userProfile.getProperty(ARGEO_USER_ID).getString();
+               // home is defined as the parent of the profile
+               homePath = userProfile.getParent().getPath();
+               securityWorkspace = userProfile.getSession().getWorkspace().getName();
+       }
+
+       /**
+        * Convenience constructor
+        * 
+        * @param session
+        *            the security session
+        * @param username
+        *            the username
+        * @param password
+        *            the password, can be null
+        * @param authorities
+        *            the granted authorities
+        */
+       public JcrUserDetails(Session session, String username, String password,
+                       GrantedAuthority[] authorities) throws RepositoryException {
+               this(UserJcrUtils.getUserProfile(session, username),
+                               password != null ? password : "", authorities);
+       }
+
+       /**
+        * Check the account status in JCR, throwing the exceptions expected by
+        * Spring security if needed.
+        */
+       public static void checkAccountStatus(Node userProfile) {
+               try {
+                       if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean())
+                               throw new DisabledException(userProfile.getPath()
+                                               + " is disabled");
+                       if (!userProfile.getProperty(ARGEO_ACCOUNT_NON_LOCKED).getBoolean())
+                               throw new LockedException(userProfile.getPath() + " is locked");
+               } catch (RepositoryException e) {
+                       throw new BadCredentialsException("Cannot check account status", e);
+               }
+       }
+
+       /** Clone immutable with new roles */
+       public JcrUserDetails cloneWithNewRoles(List<String> roles) {
+               List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+               for (String role : roles) {
+                       authorities.add(new GrantedAuthorityImpl(role));
+               }
+               return new JcrUserDetails(securityWorkspace, homePath, getUsername(),
+                               getPassword(), isEnabled(), isAccountNonExpired(),
+                               isAccountNonExpired(), isAccountNonLocked(),
+                               authorities.toArray(new GrantedAuthority[authorities.size()]));
+       }
+
+       /** Clone immutable with new password */
+       public JcrUserDetails cloneWithNewPassword(String password) {
+               return new JcrUserDetails(securityWorkspace, homePath, getUsername(),
+                               password, isEnabled(), isAccountNonExpired(),
+                               isAccountNonExpired(), isAccountNonLocked(), getAuthorities());
+       }
+
+       public String getHomePath() {
+               return homePath;
+       }
+
+       /** Not yet API */
+       public String getSecurityWorkspace() {
+               return securityWorkspace;
+       }
+
+       /** The human readable name of this user */
+       public String getDisplayName() {
+               return displayName;
+       }
+
+       @Override
+       public String toString() {
+               return getDisplayName();
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrAuthenticationProvider.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrAuthenticationProvider.java
new file mode 100644 (file)
index 0000000..aa95e32
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.jcr;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.security.OsAuthenticationToken;
+import org.argeo.security.SecurityUtils;
+import org.argeo.security.core.OsAuthenticationProvider;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+import org.springframework.security.userdetails.UserDetails;
+
+/** Relies on OS to authenticate and additionally setup JCR */
+public class OsJcrAuthenticationProvider extends OsAuthenticationProvider {
+       private Repository repository;
+       private Session nodeSession;
+
+       private UserDetails userDetails;
+       private JcrSecurityModel jcrSecurityModel = new SimpleJcrSecurityModel();
+
+       private final static String JVM_OSUSER = System.getProperty("user.name");
+
+       public void init() {
+               try {
+                       nodeSession = repository.login();
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot initialize", e);
+               }
+       }
+
+       public void destroy() {
+               JcrUtils.logoutQuietly(nodeSession);
+       }
+
+       public Authentication authenticate(Authentication authentication)
+                       throws AuthenticationException {
+               if (authentication instanceof UsernamePasswordAuthenticationToken) {
+                       // deal with remote access to internal server
+                       // FIXME very primitive and unsecure at this sSession adminSession
+                       // =tage
+                       // consider using the keyring for username / password authentication
+                       // or certificate
+                       UsernamePasswordAuthenticationToken upat = (UsernamePasswordAuthenticationToken) authentication;
+                       if (!upat.getPrincipal().toString().equals(JVM_OSUSER))
+                               throw new BadCredentialsException("Wrong credentials");
+                       UsernamePasswordAuthenticationToken authen = new UsernamePasswordAuthenticationToken(
+                                       authentication.getPrincipal(),
+                                       authentication.getCredentials(), getBaseAuthorities());
+                       authen.setDetails(userDetails);
+                       return authen;
+               } else if (authentication instanceof OsAuthenticationToken) {
+                       OsAuthenticationToken authen = (OsAuthenticationToken) super
+                                       .authenticate(authentication);
+                       try {
+                               // WARNING: at this stage we assume that the java properties
+                               // will have the same value
+                               GrantedAuthority[] authorities = getBaseAuthorities();
+                               String username = JVM_OSUSER;
+                               Node userProfile = jcrSecurityModel.sync(nodeSession, username,
+                                               SecurityUtils.authoritiesToStringList(authorities));
+                               JcrUserDetails.checkAccountStatus(userProfile);
+
+                               userDetails = new JcrUserDetails(userProfile, authen
+                                               .getCredentials().toString(), authorities);
+                               authen.setDetails(userDetails);
+                               return authen;
+                       } catch (RepositoryException e) {
+                               JcrUtils.discardQuietly(nodeSession);
+                               throw new ArgeoException(
+                                               "Unexpected exception when synchronizing OS and JCR security ",
+                                               e);
+                       }
+               } else {
+                       throw new ArgeoException("Unsupported authentication "
+                                       + authentication.getClass());
+               }
+       }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setJcrSecurityModel(JcrSecurityModel jcrSecurityModel) {
+               this.jcrSecurityModel = jcrSecurityModel;
+       }
+
+       @SuppressWarnings("rawtypes")
+       public boolean supports(Class authentication) {
+               return OsAuthenticationToken.class.isAssignableFrom(authentication)
+                               || UsernamePasswordAuthenticationToken.class
+                                               .isAssignableFrom(authentication);
+       }
+}
\ No newline at end of file
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrUserAdminService.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrUserAdminService.java
new file mode 100644 (file)
index 0000000..c25bdb8
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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.jcr;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.security.UserAdminService;
+import org.springframework.dao.DataAccessException;
+import org.springframework.security.userdetails.User;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.UsernameNotFoundException;
+
+/**
+ * Dummy user service to be used when running as a single OS user (typically
+ * desktop). TODO integrate with JCR user / groups
+ */
+public class OsJcrUserAdminService implements UserAdminService {
+       private Repository repository;
+
+       /** In memory roles provided by applications. */
+       private List<String> roles = new ArrayList<String>();
+
+       // private Session adminSession;
+
+       public void init() {
+               // try {
+               // adminSession = repository.login();
+               // } catch (RepositoryException e) {
+               // throw new ArgeoException("Cannot initialize", e);
+               // }
+       }
+
+       public void destroy() {
+               // JcrUtils.logoutQuietly(adminSession);
+       }
+
+       /** <b>Unsupported</b> */
+       public void createUser(UserDetails user) {
+               throw new UnsupportedOperationException();
+       }
+
+       /** Does nothing */
+       public void updateUser(UserDetails user) {
+
+       }
+
+       /** <b>Unsupported</b> */
+       public void deleteUser(String username) {
+               throw new UnsupportedOperationException();
+       }
+
+       /** <b>Unsupported</b> */
+       public void changePassword(String oldPassword, String newPassword) {
+               throw new UnsupportedOperationException();
+       }
+
+       public boolean userExists(String username) {
+               if (getSPropertyUsername().equals(username))
+                       return true;
+               else
+                       return false;
+       }
+
+       public UserDetails loadUserByUsername(String username)
+                       throws UsernameNotFoundException, DataAccessException {
+               if (getSPropertyUsername().equals(username)) {
+                       UserDetails userDetails;
+                       if (repository != null) {
+                               Session adminSession = null;
+                               try {
+                                       adminSession = repository.login();
+                                       Node userProfile = UserJcrUtils.getUserProfile(
+                                                       adminSession, username);
+                                       userDetails = new JcrUserDetails(userProfile, "",
+                                                       OsJcrAuthenticationProvider.getBaseAuthorities());
+                               } catch (RepositoryException e) {
+                                       throw new ArgeoException(
+                                                       "Cannot retrieve user profile for " + username, e);
+                               } finally {
+                                       JcrUtils.logoutQuietly(adminSession);
+                               }
+                       } else {
+                               userDetails = new User(username, "", true, true, true, true,
+                                               OsJcrAuthenticationProvider.getBaseAuthorities());
+                       }
+                       return userDetails;
+               } else {
+                       throw new UnsupportedOperationException();
+               }
+       }
+
+       protected final String getSPropertyUsername() {
+               return System.getProperty("user.name");
+       }
+
+       public Set<String> listUsers() {
+               Set<String> set = new HashSet<String>();
+               set.add(getSPropertyUsername());
+               return set;
+       }
+
+       public Set<String> listUsersInRole(String role) {
+               Set<String> set = new HashSet<String>();
+               set.add(getSPropertyUsername());
+               return set;
+       }
+
+       /** Does nothing */
+       public void synchronize() {
+       }
+
+       /** <b>Unsupported</b> */
+       public void newRole(String role) {
+               roles.add(role);
+       }
+
+       public Set<String> listEditableRoles() {
+               return new HashSet<String>(roles);
+       }
+
+       /** <b>Unsupported</b> */
+       public void deleteRole(String role) {
+               roles.remove(role);
+       }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrAuthenticationProvider.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrAuthenticationProvider.java
new file mode 100644 (file)
index 0000000..87208b2
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * 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.jcr;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.Value;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.security.NodeAuthenticationToken;
+import org.osgi.framework.BundleContext;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.providers.AuthenticationProvider;
+
+/** Connects to a JCR repository and delegates authentication to it. */
+public class RemoteJcrAuthenticationProvider implements AuthenticationProvider,
+               ArgeoNames {
+       private RepositoryFactory repositoryFactory;
+       private BundleContext bundleContext;
+
+       public final static String ROLE_REMOTE = "ROLE_REMOTE";
+
+       public Authentication authenticate(Authentication authentication)
+                       throws AuthenticationException {
+               NodeAuthenticationToken siteAuth = (NodeAuthenticationToken) authentication;
+               String url = siteAuth.getUrl();
+               if (url == null)// TODO? login on own node
+                       throw new ArgeoException("No url set in " + siteAuth);
+               Session session;
+
+               Node userProfile;
+               try {
+                       SimpleCredentials sp = new SimpleCredentials(siteAuth.getName(),
+                                       siteAuth.getCredentials().toString().toCharArray());
+                       // get repository
+                       Repository repository = new RemoteJcrRepositoryWrapper(
+                                       repositoryFactory, url, sp);
+                       if (bundleContext != null) {
+                               Dictionary<String, String> serviceProperties = new Hashtable<String, String>();
+                               serviceProperties.put(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS,
+                                               ArgeoJcrConstants.ALIAS_NODE);
+                               serviceProperties
+                                               .put(ArgeoJcrConstants.JCR_REPOSITORY_URI, url);
+                               bundleContext.registerService(Repository.class.getName(),
+                                               repository, serviceProperties);
+                       }
+                       // Repository repository = ArgeoJcrUtils.getRepositoryByUri(
+                       // repositoryFactory, url);
+                       // if (repository == null)
+                       // throw new ArgeoException("Cannot connect to " + url);
+
+                       session = repository.login(sp, null);
+
+                       userProfile = UserJcrUtils.getUserProfile(session, sp.getUserID());
+                       JcrUserDetails.checkAccountStatus(userProfile);
+
+                       // Node userHome = UserJcrUtils.getUserHome(session);
+                       // if (userHome == null ||
+                       // !userHome.hasNode(ArgeoNames.ARGEO_PROFILE))
+                       // throw new ArgeoException("No profile for user "
+                       // + siteAuth.getName() + " in security workspace "
+                       // + siteAuth.getSecurityWorkspace() + " of "
+                       // + siteAuth.getUrl());
+                       // userProfile = userHome.getNode(ArgeoNames.ARGEO_PROFILE);
+               } catch (RepositoryException e) {
+                       throw new BadCredentialsException(
+                                       "Cannot authenticate " + siteAuth, e);
+               }
+
+               try {
+                       // Node userHome = UserJcrUtils.getUserHome(session);
+                       // retrieve remote roles
+                       List<GrantedAuthority> authoritiesList = new ArrayList<GrantedAuthority>();
+                       if (userProfile != null
+                                       && userProfile.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
+                               Value[] roles = userProfile.getProperty(
+                                               ArgeoNames.ARGEO_REMOTE_ROLES).getValues();
+                               for (int i = 0; i < roles.length; i++)
+                                       authoritiesList.add(new GrantedAuthorityImpl(roles[i]
+                                                       .getString()));
+                       }
+                       authoritiesList.add(new GrantedAuthorityImpl(ROLE_REMOTE));
+
+                       // create authenticated objects
+                       GrantedAuthority[] authorities = authoritiesList
+                                       .toArray(new GrantedAuthority[authoritiesList.size()]);
+                       JcrUserDetails userDetails = new JcrUserDetails(userProfile,
+                                       siteAuth.getCredentials().toString(), authorities);
+                       NodeAuthenticationToken authenticated = new NodeAuthenticationToken(
+                                       siteAuth, authorities);
+                       authenticated.setDetails(userDetails);
+                       return authenticated;
+               } catch (RepositoryException e) {
+                       throw new ArgeoException(
+                                       "Unexpected exception when authenticating to " + url, e);
+               }
+       }
+
+       @SuppressWarnings("rawtypes")
+       public boolean supports(Class authentication) {
+               return NodeAuthenticationToken.class.isAssignableFrom(authentication);
+       }
+
+       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
+               this.repositoryFactory = repositoryFactory;
+       }
+
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrRepositoryWrapper.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrRepositoryWrapper.java
new file mode 100644 (file)
index 0000000..f0ad3a3
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * 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.jcr;
+
+import javax.jcr.Credentials;
+import javax.jcr.LoginException;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoJcrUtils;
+import org.argeo.jcr.JcrRepositoryWrapper;
+import org.argeo.security.NodeAuthenticationToken;
+import org.argeo.security.SystemAuthentication;
+import org.springframework.security.Authentication;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+
+/**
+ * Wrapper around a remote Jackrabbit repository which allows to simplify
+ * configuration and intercept some actions. It exposes itself as a
+ * {@link Repository}.
+ */
+public class RemoteJcrRepositoryWrapper extends JcrRepositoryWrapper {
+       private final static Log log = LogFactory
+                       .getLog(RemoteJcrRepositoryWrapper.class);
+
+       private String uri = null;
+
+       private RepositoryFactory repositoryFactory;
+
+       // remote
+       private Credentials remoteSystemCredentials = null;
+
+       /**
+        * Empty constructor, {@link #init()} should be called after properties have
+        * been set
+        */
+       public RemoteJcrRepositoryWrapper() {
+       }
+
+       /**
+        * Embedded constructor, calling the {@link #init()} method.
+        * 
+        * @param alias
+        *            if not null the repository will be published under this alias
+        */
+       public RemoteJcrRepositoryWrapper(RepositoryFactory repositoryFactory,
+                       String uri, Credentials remoteSystemCredentials) {
+               this.repositoryFactory = repositoryFactory;
+               this.uri = uri;
+               this.remoteSystemCredentials = remoteSystemCredentials;
+               init();
+       }
+
+       public void init() {
+               Repository repository = createJackrabbitRepository();
+               setRepository(repository);
+       }
+
+       /** Actually creates the new repository. */
+       protected Repository createJackrabbitRepository() {
+               long begin = System.currentTimeMillis();
+               try {
+                       if (uri == null || uri.trim().equals(""))
+                               throw new ArgeoException("Remote URI not set");
+
+                       Repository repository = ArgeoJcrUtils.getRepositoryByUri(
+                                       repositoryFactory, uri);
+                       if (repository == null)
+                               throw new ArgeoException("Remote JCR repository " + uri
+                                               + " not found");
+                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+                       log.info("Created remote JCR repository in " + duration
+                                       + " s from URI " + uri);
+                       // we assume that the data model of the remote repository has
+                       // been properly initialized
+                       return repository;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot create remote JCR repository "
+                                       + uri, e);
+               }
+       }
+
+       /** Shutdown the repository */
+       public void destroy() throws Exception {
+               super.destroy();
+       }
+
+       /** Central login method */
+       public Session login(Credentials credentials, String workspaceName)
+                       throws LoginException, NoSuchWorkspaceException,
+                       RepositoryException {
+
+               // retrieve credentials for remote
+               if (credentials == null) {
+                       Authentication authentication = SecurityContextHolder.getContext()
+                                       .getAuthentication();
+                       if (authentication != null) {
+                               if (authentication instanceof UsernamePasswordAuthenticationToken) {
+                                       UsernamePasswordAuthenticationToken upat = (UsernamePasswordAuthenticationToken) authentication;
+                                       credentials = new SimpleCredentials(upat.getName(), upat
+                                                       .getCredentials().toString().toCharArray());
+                               } else if ((authentication instanceof SystemAuthentication)
+                                               || (authentication instanceof NodeAuthenticationToken)) {
+                                       credentials = remoteSystemCredentials;
+                               }
+                       }
+               }
+
+               return super.login(credentials, workspaceName);
+       }
+
+       public void setUri(String uri) {
+               this.uri = uri;
+       }
+
+       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
+               this.repositoryFactory = repositoryFactory;
+       }
+
+       public void setRemoteSystemCredentials(Credentials remoteSystemCredentials) {
+               this.remoteSystemCredentials = remoteSystemCredentials;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SecureThreadBoundSession.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SecureThreadBoundSession.java
new file mode 100644 (file)
index 0000000..b398774
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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.jcr;
+
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.jcr.spring.ThreadBoundSession;
+import org.springframework.security.Authentication;
+import org.springframework.security.context.SecurityContextHolder;
+
+/**
+ * Thread bounded JCR session factory which checks authentication and is
+ * autoconfigured in Spring.
+ */
+public class SecureThreadBoundSession extends ThreadBoundSession {
+       private final static Log log = LogFactory
+                       .getLog(SecureThreadBoundSession.class);
+
+       @Override
+       protected Session preCall(Session session) {
+               Authentication authentication = SecurityContextHolder.getContext()
+                               .getAuthentication();
+               if (authentication != null) {
+                       String userID = session.getUserID();
+                       String currentUserName = authentication.getName();
+                       if (currentUserName != null) {
+                               if (!userID.equals(currentUserName)) {
+                                       log.warn("Current session has user ID " + userID
+                                                       + " while logged is user is " + currentUserName
+                                                       + "(authentication=" + authentication + ")"
+                                                       + ". Re-login.");
+                                       // TODO throw an exception
+                                       return login();
+                               }
+                       }
+               }
+               return super.preCall(session);
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SimpleJcrSecurityModel.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SimpleJcrSecurityModel.java
new file mode 100644 (file)
index 0000000..fc01587
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * 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.jcr;
+
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.security.Privilege;
+import javax.jcr.version.VersionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+
+/**
+ * Manages data expected by the Argeo security model, such as user home and
+ * profile.
+ */
+public class SimpleJcrSecurityModel implements JcrSecurityModel {
+       private final static Log log = LogFactory
+                       .getLog(SimpleJcrSecurityModel.class);
+       // ArgeoNames not implemented as interface in order to ease derivation by
+       // Jackrabbit bundles
+
+       /** The home base path. */
+       private String homeBasePath = "/home";
+
+       public synchronized Node sync(Session session, String username,
+                       List<String> roles) {
+               // TODO check user name validity (e.g. should not start by ROLE_)
+
+               try {
+                       Node userHome = UserJcrUtils.getUserHome(session, username);
+                       if (userHome == null) {
+                               String homePath = generateUserPath(homeBasePath, username);
+                               userHome = JcrUtils.mkdirs(session, homePath);
+                               // userHome = JcrUtils.mkfolders(session, homePath);
+                               userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME);
+                               userHome.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+                               session.save();
+
+                               JcrUtils.clearAccessControList(session, homePath, username);
+                               JcrUtils.addPrivilege(session, homePath, username,
+                                               Privilege.JCR_ALL);
+                       } else {
+                               // for backward compatibility with pre 1.0 security model
+                               if (userHome.hasNode(ArgeoNames.ARGEO_PROFILE)) {
+                                       userHome.getNode(ArgeoNames.ARGEO_PROFILE).remove();
+                                       userHome.getSession().save();
+                               }
+                       }
+
+                       // Remote roles
+                       if (roles != null) {
+                               // writeRemoteRoles(userHome, roles);
+                       }
+
+                       Node userProfile = UserJcrUtils.getUserProfile(session, username);
+                       if (userProfile == null) {
+                               String personPath = generateUserPath(
+                                               ArgeoJcrConstants.PEOPLE_BASE_PATH, username);
+                               Node personBase = JcrUtils.mkdirs(session, personPath);
+                               userProfile = personBase.addNode(ArgeoNames.ARGEO_PROFILE);
+                               userProfile.addMixin(ArgeoTypes.ARGEO_USER_PROFILE);
+                               userProfile.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+                               userProfile.setProperty(ArgeoNames.ARGEO_ENABLED, true);
+                               userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED,
+                                               true);
+                               userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED,
+                                               true);
+                               userProfile.setProperty(
+                                               ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED, true);
+                               session.save();
+
+                               JcrUtils.clearAccessControList(session, userProfile.getPath(),
+                                               username);
+                               JcrUtils.addPrivilege(session, userProfile.getPath(), username,
+                                               Privilege.JCR_READ);
+
+                               VersionManager versionManager = session.getWorkspace()
+                                               .getVersionManager();
+                               if (versionManager.isCheckedOut(userProfile.getPath()))
+                                       versionManager.checkin(userProfile.getPath());
+
+                       }
+
+                       // Remote roles
+                       if (roles != null) {
+                               writeRemoteRoles(userProfile, roles);
+                       }
+                       return userProfile;
+               } catch (RepositoryException e) {
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoException("Cannot sync node security model for "
+                                       + username, e);
+               }
+       }
+
+       /** Generate path for a new user home */
+       protected String generateUserPath(String base, String username) {
+               int atIndex = username.indexOf('@');
+               if (atIndex > 0) {
+                       String domain = username.substring(0, atIndex);
+                       String name = username.substring(atIndex + 1);
+                       return base + '/' + JcrUtils.firstCharsToPath(domain, 2) + '/'
+                                       + domain + '/' + JcrUtils.firstCharsToPath(name, 2) + '/'
+                                       + name;
+               } else if (atIndex == 0 || atIndex == (username.length() - 1)) {
+                       throw new ArgeoException("Unsupported username " + username);
+               } else {
+                       return base + '/' + JcrUtils.firstCharsToPath(username, 2) + '/'
+                                       + username;
+               }
+       }
+
+       /** Write remote roles used by remote access in the home directory */
+       protected void writeRemoteRoles(Node userHome, List<String> roles)
+                       throws RepositoryException {
+               boolean writeRoles = false;
+               if (userHome.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
+                       Value[] remoteRoles = userHome.getProperty(
+                                       ArgeoNames.ARGEO_REMOTE_ROLES).getValues();
+                       if (remoteRoles.length != roles.size())
+                               writeRoles = true;
+                       else
+                               for (int i = 0; i < remoteRoles.length; i++)
+                                       if (!remoteRoles[i].getString().equals(roles.get(i)))
+                                               writeRoles = true;
+               } else
+                       writeRoles = true;
+
+               if (writeRoles) {
+                       userHome.getSession().getWorkspace().getVersionManager()
+                                       .checkout(userHome.getPath());
+                       String[] roleIds = roles.toArray(new String[roles.size()]);
+                       userHome.setProperty(ArgeoNames.ARGEO_REMOTE_ROLES, roleIds);
+                       JcrUtils.updateLastModified(userHome);
+                       userHome.getSession().save();
+                       userHome.getSession().getWorkspace().getVersionManager()
+                                       .checkin(userHome.getPath());
+                       if (log.isDebugEnabled())
+                               log.debug("Wrote remote roles " + roles + " for "
+                                               + userHome.getProperty(ArgeoNames.ARGEO_USER_ID));
+               }
+
+       }
+
+       public void setHomeBasePath(String homeBasePath) {
+               this.homeBasePath = homeBasePath;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java
new file mode 100644 (file)
index 0000000..37dc986
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.jcr.rememberme;
+
+import java.util.Date;
+
+import org.springframework.security.ui.rememberme.PersistentRememberMeToken;
+import org.springframework.security.ui.rememberme.PersistentTokenRepository;
+
+public class JcrPersistentTokenRepository implements PersistentTokenRepository {
+
+       public void createNewToken(PersistentRememberMeToken token) {
+               // TODO Auto-generated method stub
+
+       }
+
+       public void updateToken(String series, String tokenValue, Date lastUsed) {
+               // TODO Auto-generated method stub
+
+       }
+
+       public PersistentRememberMeToken getTokenForSeries(String seriesId) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       public void removeUserTokens(String username) {
+               // TODO Auto-generated method stub
+
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/log4j/SecureLogger.java b/trunk/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/log4j/SecureLogger.java
new file mode 100644 (file)
index 0000000..1da9857
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * 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.log4j;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.spi.LoggingEvent;
+import org.argeo.ArgeoException;
+import org.argeo.ArgeoLogListener;
+import org.argeo.ArgeoLogger;
+import org.argeo.security.SecurityUtils;
+
+/** Not meant to be used directly in standard log4j config */
+public class SecureLogger implements ArgeoLogger {
+
+       private Boolean disabled = false;
+
+       private String level = null;
+
+       private Level log4jLevel = null;
+       // private Layout layout;
+
+       private Properties configuration;
+
+       private AppenderImpl appender;
+
+       private final List<ArgeoLogListener> everythingListeners = Collections
+                       .synchronizedList(new ArrayList<ArgeoLogListener>());
+       private final List<ArgeoLogListener> allUsersListeners = Collections
+                       .synchronizedList(new ArrayList<ArgeoLogListener>());
+       private final Map<String, List<ArgeoLogListener>> userListeners = Collections
+                       .synchronizedMap(new HashMap<String, List<ArgeoLogListener>>());
+
+       private BlockingQueue<LogEvent> events;
+       private LogDispatcherThread logDispatcherThread = new LogDispatcherThread();
+
+       private Integer maxLastEventsCount = 10 * 1000;
+
+       /** Marker to prevent stack overflow */
+       private ThreadLocal<Boolean> dispatching = new ThreadLocal<Boolean>() {
+
+               @Override
+               protected Boolean initialValue() {
+                       return false;
+               }
+       };
+
+       public void init() {
+               try {
+                       events = new LinkedBlockingQueue<LogEvent>();
+
+                       // if (layout != null)
+                       // setLayout(layout);
+                       // else
+                       // setLayout(new PatternLayout(pattern));
+                       appender = new AppenderImpl();
+                       reloadConfiguration();
+                       Logger.getRootLogger().addAppender(appender);
+
+                       logDispatcherThread = new LogDispatcherThread();
+                       logDispatcherThread.start();
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot initialize log4j");
+               }
+       }
+
+       public void destroy() throws Exception {
+               Logger.getRootLogger().removeAppender(appender);
+               allUsersListeners.clear();
+               for (List<ArgeoLogListener> lst : userListeners.values())
+                       lst.clear();
+               userListeners.clear();
+
+               events.clear();
+               events = null;
+               logDispatcherThread.interrupt();
+       }
+
+       // public void setLayout(Layout layout) {
+       // this.layout = layout;
+       // }
+
+       public synchronized void register(ArgeoLogListener listener,
+                       Integer numberOfPreviousEvents) {
+               String username = SecurityUtils.getCurrentThreadUsername();
+               if (username == null)
+                       throw new ArgeoException(
+                                       "Only authenticated users can register a log listener");
+
+               if (!userListeners.containsKey(username)) {
+                       List<ArgeoLogListener> lst = Collections
+                                       .synchronizedList(new ArrayList<ArgeoLogListener>());
+                       userListeners.put(username, lst);
+               }
+               userListeners.get(username).add(listener);
+               List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(username,
+                               numberOfPreviousEvents);
+               for (LogEvent evt : lastEvents)
+                       dispatchEvent(listener, evt);
+       }
+
+       public synchronized void registerForAll(ArgeoLogListener listener,
+                       Integer numberOfPreviousEvents, boolean everything) {
+               if (everything)
+                       everythingListeners.add(listener);
+               else
+                       allUsersListeners.add(listener);
+               List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(null,
+                               numberOfPreviousEvents);
+               for (LogEvent evt : lastEvents)
+                       if (everything || evt.getUsername() != null)
+                               dispatchEvent(listener, evt);
+       }
+
+       public synchronized void unregister(ArgeoLogListener listener) {
+               String username = SecurityUtils.getCurrentThreadUsername();
+               if (!userListeners.containsKey(username))
+                       throw new ArgeoException("No user listeners " + listener
+                                       + " registered for user " + username);
+               if (!userListeners.get(username).contains(listener))
+                       throw new ArgeoException("No user listeners " + listener
+                                       + " registered for user " + username);
+               userListeners.get(username).remove(listener);
+               if (userListeners.get(username).isEmpty())
+                       userListeners.remove(username);
+
+       }
+
+       public synchronized void unregisterForAll(ArgeoLogListener listener) {
+               everythingListeners.remove(listener);
+               allUsersListeners.remove(listener);
+       }
+
+       /** For development purpose, since using regular logging is not easy here */
+       static void stdOut(Object obj) {
+               System.out.println(obj);
+       }
+
+       // public void setPattern(String pattern) {
+       // this.pattern = pattern;
+       // }
+
+       public void setDisabled(Boolean disabled) {
+               this.disabled = disabled;
+       }
+
+       public void setLevel(String level) {
+               this.level = level;
+       }
+
+       public void setConfiguration(Properties configuration) {
+               this.configuration = configuration;
+       }
+
+       public void updateConfiguration(Properties configuration) {
+               setConfiguration(configuration);
+               reloadConfiguration();
+       }
+
+       public Properties getConfiguration() {
+               return configuration;
+       }
+
+       /** Reloads configuration (if the configuration {@link Properties} is set) */
+       protected void reloadConfiguration() {
+               if (configuration != null) {
+                       LogManager.resetConfiguration();
+                       PropertyConfigurator.configure(configuration);
+               }
+       }
+
+       protected synchronized void processLoggingEvent(LogEvent event) {
+               if (disabled)
+                       return;
+
+               if (dispatching.get())
+                       return;
+
+               if (level != null && !level.trim().equals("")) {
+                       if (log4jLevel == null || !log4jLevel.toString().equals(level))
+                               try {
+                                       log4jLevel = Level.toLevel(level);
+                               } catch (Exception e) {
+                                       System.err
+                                                       .println("Log4j level could not be set for level '"
+                                                                       + level + "', resetting it to null.");
+                                       e.printStackTrace();
+                                       level = null;
+                               }
+
+                       if (log4jLevel != null
+                                       && !event.getLoggingEvent().getLevel()
+                                                       .isGreaterOrEqual(log4jLevel)) {
+                               return;
+                       }
+               }
+
+               try {
+                       // admin listeners
+                       Iterator<ArgeoLogListener> everythingIt = everythingListeners
+                                       .iterator();
+                       while (everythingIt.hasNext())
+                               dispatchEvent(everythingIt.next(), event);
+
+                       if (event.getUsername() != null) {
+                               Iterator<ArgeoLogListener> allUsersIt = allUsersListeners
+                                               .iterator();
+                               while (allUsersIt.hasNext())
+                                       dispatchEvent(allUsersIt.next(), event);
+
+                               if (userListeners.containsKey(event.getUsername())) {
+                                       Iterator<ArgeoLogListener> userIt = userListeners.get(
+                                                       event.getUsername()).iterator();
+                                       while (userIt.hasNext())
+                                               dispatchEvent(userIt.next(), event);
+                               }
+                       }
+               } catch (Exception e) {
+                       stdOut("Cannot process logging event");
+                       e.printStackTrace();
+               }
+       }
+
+       protected void dispatchEvent(ArgeoLogListener logListener, LogEvent evt) {
+               LoggingEvent event = evt.getLoggingEvent();
+               logListener.appendLog(evt.getUsername(), event.getTimeStamp(), event
+                               .getLevel().toString(), event.getLoggerName(), event
+                               .getThreadName(), event.getMessage(), event
+                               .getThrowableStrRep());
+       }
+
+       private class AppenderImpl extends AppenderSkeleton {
+               public boolean requiresLayout() {
+                       return false;
+               }
+
+               public void close() {
+               }
+
+               @Override
+               protected void append(LoggingEvent event) {
+                       if (events != null) {
+                               try {
+                                       String username = SecurityUtils.getCurrentThreadUsername();
+                                       events.put(new LogEvent(username, event));
+                               } catch (InterruptedException e) {
+                                       // silent
+                               }
+                       }
+               }
+
+       }
+
+       private class LogDispatcherThread extends Thread {
+               /** encapsulated in order to simplify concurrency management */
+               private LinkedList<LogEvent> lastEvents = new LinkedList<LogEvent>();
+
+               public LogDispatcherThread() {
+                       super("Argeo Logging Dispatcher Thread");
+               }
+
+               public void run() {
+                       while (events != null) {
+                               try {
+                                       LogEvent loggingEvent = events.take();
+                                       processLoggingEvent(loggingEvent);
+                                       addLastEvent(loggingEvent);
+                               } catch (InterruptedException e) {
+                                       if (events == null)
+                                               return;
+                               }
+                       }
+               }
+
+               protected synchronized void addLastEvent(LogEvent loggingEvent) {
+                       if (lastEvents.size() >= maxLastEventsCount)
+                               lastEvents.poll();
+                       lastEvents.add(loggingEvent);
+               }
+
+               public synchronized List<LogEvent> getLastEvents(String username,
+                               Integer maxCount) {
+                       LinkedList<LogEvent> evts = new LinkedList<LogEvent>();
+                       ListIterator<LogEvent> it = lastEvents.listIterator(lastEvents
+                                       .size());
+                       int count = 0;
+                       while (it.hasPrevious() && (count < maxCount)) {
+                               LogEvent evt = it.previous();
+                               if (username == null || username.equals(evt.getUsername())) {
+                                       evts.push(evt);
+                                       count++;
+                               }
+                       }
+                       return evts;
+               }
+       }
+
+       private class LogEvent {
+               private final String username;
+               private final LoggingEvent loggingEvent;
+
+               public LogEvent(String username, LoggingEvent loggingEvent) {
+                       super();
+                       this.username = username;
+                       this.loggingEvent = loggingEvent;
+               }
+
+               @Override
+               public int hashCode() {
+                       return loggingEvent.hashCode();
+               }
+
+               @Override
+               public boolean equals(Object obj) {
+                       return loggingEvent.equals(obj);
+               }
+
+               @Override
+               public String toString() {
+                       return username + "@ " + loggingEvent.toString();
+               }
+
+               public String getUsername() {
+                       return username;
+               }
+
+               public LoggingEvent getLoggingEvent() {
+                       return loggingEvent;
+               }
+
+       }
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/PasswordSandbox.java b/trunk/security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/PasswordSandbox.java
new file mode 100644 (file)
index 0000000..d8a0846
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.binary.Hex;
+import org.springframework.security.providers.ldap.authenticator.LdapShaPasswordEncoder;
+
+public class PasswordSandbox {
+       public static void main(String[] args) {
+               try {
+                       // Tested password
+                       String pwdPlain = "demo";
+
+                       // Check Java generated values
+                       LdapShaPasswordEncoder lspe = new LdapShaPasswordEncoder();
+                       String pwdLdapShaBase64 = lspe.encodePassword(pwdPlain, null);
+                       System.out.println("pwdLdapShaBase64:\t\t" + pwdLdapShaBase64);
+
+                       String pwdShaBase64 = pwdLdapShaBase64.substring("{SHA}".length());
+                       System.out.println("pwdShaBase64:\t\t\t" + pwdShaBase64);
+
+                       byte[] pwdShaArray = Base64.decodeBase64(pwdShaBase64.getBytes());
+                       String pwdShaHex = new String(Hex.encodeHex(pwdShaArray));
+                       System.out.println("pwdShaHex:\t\t\t" + pwdShaHex);
+
+                       // Check that we can use JavaScript generated values in Hex
+                       String jsShaHex = "89e495e7941cf9e40e6980d14a16bf023ccd4c91";
+                       System.out.println("jsShaHex:\t\t\t" + pwdShaHex);
+                       System.out.println("pwdShaHex==jsShaHex:\t\t"
+                                       + (pwdShaHex.equals(jsShaHex)));
+
+                       byte[] jsShaArray = Hex.decodeHex(jsShaHex.toCharArray());
+                       String jsShaBase64 = new String(Base64.encodeBase64(jsShaArray));
+                       System.out.println("jsShaBase64:\t\t\t" + jsShaBase64);
+                       System.out.println("pwdShaBase64==jsShaBase64:\t"
+                                       + (pwdShaBase64.equals(jsShaBase64)));
+               } catch (DecoderException e) {
+                       e.printStackTrace();
+               }
+
+       }
+
+}
\ No newline at end of file
diff --git a/trunk/security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/crypto/PasswordBasedEncryptionTest.java b/trunk/security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/crypto/PasswordBasedEncryptionTest.java
new file mode 100644 (file)
index 0000000..6973f57
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.xml.bind.DatatypeConverter;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.StreamUtils;
+import org.argeo.security.crypto.PasswordBasedEncryption;
+
+public class PasswordBasedEncryptionTest extends TestCase {
+       private final static Log log = LogFactory
+                       .getLog(PasswordBasedEncryptionTest.class);
+
+       public void testEncryptDecrypt() {
+               final String password = "test long password since they are safer";
+               PasswordBasedEncryption pbeEnc = new PasswordBasedEncryption(
+                               password.toCharArray());
+               String message = "Hello World!";
+               log.info("Password:\t'" + password + "'");
+               log.info("Message:\t'" + message + "'");
+               byte[] encrypted = pbeEnc.encryptString(message);
+               log.info("Encrypted:\t'"
+                               + DatatypeConverter.printBase64Binary(encrypted) + "'");
+               PasswordBasedEncryption pbeDec = new PasswordBasedEncryption(
+                               password.toCharArray());
+               InputStream in = null;
+               in = new ByteArrayInputStream(encrypted);
+               String decrypted = pbeDec.decryptAsString(in);
+               log.info("Decrypted:\t'" + decrypted + "'");
+               StreamUtils.closeQuietly(in);
+               assertEquals(message, decrypted);
+       }
+
+       public void testPBEWithMD5AndDES() throws Exception {
+               String password = "test";
+               String message = "Hello World!";
+
+               byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+                               (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
+
+               int count = 1024;
+
+               String cipherAlgorithm = "PBEWithMD5AndDES";
+               String secretKeyAlgorithm = "PBEWithMD5AndDES";
+               SecretKeyFactory keyFac = SecretKeyFactory
+                               .getInstance(secretKeyAlgorithm);
+               PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
+               PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
+               SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
+               Cipher ecipher = Cipher.getInstance(cipherAlgorithm);
+               ecipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
+               Cipher dcipher = Cipher.getInstance(cipherAlgorithm);
+               dcipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
+
+               byte[] encrypted = ecipher.doFinal(message.getBytes());
+               byte[] decrypted = dcipher.doFinal(encrypted);
+               assertEquals(message, new String(decrypted));
+
+       }
+
+       public void testPBEWithSHA1AndAES() throws Exception {
+               String password = "test";
+               String message = "Hello World!";
+
+               byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+                               (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
+               byte[] iv = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+                               (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99,
+                               (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+                               (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
+
+               int count = 1024;
+               // int keyLength = 256;
+               int keyLength = 128;
+
+               String cipherAlgorithm = "AES/CBC/PKCS5Padding";
+               String secretKeyAlgorithm = "PBKDF2WithHmacSHA1";
+               SecretKeyFactory keyFac = SecretKeyFactory
+                               .getInstance(secretKeyAlgorithm);
+               PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt,
+                               count, keyLength);
+               SecretKey tmp = keyFac.generateSecret(pbeKeySpec);
+               SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
+               Cipher ecipher = Cipher.getInstance(cipherAlgorithm);
+               ecipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
+
+               // decrypt
+               keyFac = SecretKeyFactory.getInstance(secretKeyAlgorithm);
+               pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, count,
+                               keyLength);
+               tmp = keyFac.generateSecret(pbeKeySpec);
+               secret = new SecretKeySpec(tmp.getEncoded(), "AES");
+               // AlgorithmParameters params = ecipher.getParameters();
+               // byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
+               Cipher dcipher = Cipher.getInstance(cipherAlgorithm);
+               dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
+
+               byte[] encrypted = ecipher.doFinal(message.getBytes());
+               byte[] decrypted = dcipher.doFinal(encrypted);
+               assertEquals(message, new String(decrypted));
+
+               ByteArrayOutputStream out = new ByteArrayOutputStream();
+               CipherOutputStream cipherOut = new CipherOutputStream(out, ecipher);
+               cipherOut.write(message.getBytes());
+               StreamUtils.closeQuietly(cipherOut);
+               byte[] enc = out.toByteArray();
+
+               ByteArrayInputStream in = new ByteArrayInputStream(enc);
+               CipherInputStream cipherIn = new CipherInputStream(in, dcipher);
+               ByteArrayOutputStream dec = new ByteArrayOutputStream();
+               StreamUtils.copy(cipherIn, dec);
+               assertEquals(message, new String(dec.toByteArray()));
+       }
+}
diff --git a/trunk/security/runtime/org.argeo.security.core/src/test/resources/log4j.properties b/trunk/security/runtime/org.argeo.security.core/src/test/resources/log4j.properties
new file mode 100644 (file)
index 0000000..b33daa9
--- /dev/null
@@ -0,0 +1,28 @@
+log4j.rootLogger=WARN, console
+
+## Levels
+log4j.logger.org.argeo=DEBUG
+
+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/trunk/security/runtime/org.argeo.security.core/src/test/resources/org/argeo/security/json/gandalf2.json b/trunk/security/runtime/org.argeo.security.core/src/test/resources/org/argeo/security/json/gandalf2.json
new file mode 100644 (file)
index 0000000..7391822
--- /dev/null
@@ -0,0 +1 @@
+{"roles":["ROLE_ADMIN","ROLE_USER"],"userNatures":[{"description":"Superuser","email":"admin@localhost","firstName":"Gandalf","lastName":"User","type":"org.argeo.security.nature.SimpleUserNature"},{"mobile":null,"telephoneNumber":null,"type":"org.argeo.security.nature.CoworkerNature"}],"username":"gandalf2","enabled":true,"password":"{SHA}ieSV55Qc+eQOaYDRSha/AjzNTJE=","authorities":[{"authority":"ROLE_ADMIN"},{"authority":"ROLE_USER"}],"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true}
\ No newline at end of file
diff --git a/trunk/security/runtime/org.argeo.security.jackrabbit/.classpath b/trunk/security/runtime/org.argeo.security.jackrabbit/.classpath
new file mode 100644 (file)
index 0000000..5641c7c
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/security/runtime/org.argeo.security.jackrabbit/.project b/trunk/security/runtime/org.argeo.security.jackrabbit/.project
new file mode 100644 (file)
index 0000000..35bcca2
--- /dev/null
@@ -0,0 +1,28 @@
+<?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/trunk/security/runtime/org.argeo.security.jackrabbit/build.properties b/trunk/security/runtime/org.argeo.security.jackrabbit/build.properties
new file mode 100644 (file)
index 0000000..5fc538b
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .
diff --git a/trunk/security/runtime/org.argeo.security.jackrabbit/pom.xml b/trunk/security/runtime/org.argeo.security.jackrabbit/pom.xml
new file mode 100644 (file)
index 0000000..6295fe3
--- /dev/null
@@ -0,0 +1,58 @@
+<?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.security</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.jackrabbit</artifactId>
+       <name>Commons Security Jackrabbit</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Fragment-Host>org.apache.jackrabbit</Fragment-Host>
+                                               <Export-Package>org.argeo.security.jackrabbit.*</Export-Package>
+                                               <Import-Package>*,org.springframework.core,org.argeo.jcr</Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.core</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.jcr</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.jackrabbit</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.security.core</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoAccessManager.java b/trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/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/trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoLoginModule.java b/trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoLoginModule.java
new file mode 100644 (file)
index 0000000..43c5440
--- /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.security.jackrabbit;
+
+import java.security.Principal;
+import java.security.acl.Group;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+
+import org.apache.jackrabbit.core.security.AnonymousPrincipal;
+import org.apache.jackrabbit.core.security.authentication.AbstractLoginModule;
+import org.apache.jackrabbit.core.security.authentication.Authentication;
+import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
+import org.argeo.security.SystemAuthentication;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
+
+/** Jackrabbit login mechanism based on Spring Security */
+public class ArgeoLoginModule extends AbstractLoginModule {
+       private String adminRole = "ROLE_ADMIN";
+
+       @SuppressWarnings("unused")
+       @Override
+       public boolean login() throws LoginException {
+               boolean loginOk = super.login();
+               if (!loginOk) {
+                       org.springframework.security.Authentication authen = (org.springframework.security.Authentication) SecurityContextHolder
+                                       .getContext().getAuthentication();
+               }
+               return loginOk;
+       }
+
+       @SuppressWarnings("unused")
+       @Override
+       public boolean commit() throws LoginException {
+               boolean commitOk = super.commit();
+               if (!commitOk) {
+                       org.springframework.security.Authentication authen = (org.springframework.security.Authentication) SecurityContextHolder
+                                       .getContext().getAuthentication();
+               }
+               return commitOk;
+       }
+
+       /**
+        * Returns the Spring {@link org.springframework.security.Authentication}
+        * (which can be null)
+        */
+       @Override
+       protected Principal getPrincipal(Credentials credentials) {
+               org.springframework.security.Authentication authen = SecurityContextHolder
+                               .getContext().getAuthentication();
+               return authen;
+       }
+
+       protected Set<Principal> getPrincipals() {
+               // clear already registered Jackrabbit principals
+               // clearPrincipals(AdminPrincipal.class);
+               // clearPrincipals(AnonymousPrincipal.class);
+               // clearPrincipals(GrantedAuthorityPrincipal.class);
+
+               return syncPrincipals();
+       }
+
+       protected Set<Principal> syncPrincipals() {
+               // use linked HashSet instead of HashSet in order to maintain the order
+               // of principals (as in the Subject).
+               org.springframework.security.Authentication authen = (org.springframework.security.Authentication) principal;
+
+               Set<Principal> principals = new LinkedHashSet<Principal>();
+               principals.add(authen);
+
+               if (authen instanceof SystemAuthentication) {
+                       principals.add(new AdminPrincipal(authen.getName()));
+                       principals.add(new ArgeoSystemPrincipal(authen.getName()));
+               } else if (authen instanceof AnonymousAuthenticationToken) {
+                       principals.add(new AnonymousPrincipal());
+               } else {
+                       for (GrantedAuthority ga : authen.getAuthorities()) {
+                               principals.add(new GrantedAuthorityPrincipal(ga));
+                               // FIXME: make it more generic
+                               if (adminRole.equals(ga.getAuthority()))
+                                       principals.add(new AdminPrincipal(authen.getName()));
+                       }
+               }
+
+               // remove previous credentials
+               Set<SimpleCredentials> thisCredentials = subject
+                               .getPublicCredentials(SimpleCredentials.class);
+               if (thisCredentials != null)
+                       thisCredentials.clear();
+               // override credentials since we did not used the one passed to us
+               // credentials = new SimpleCredentials(authen.getName(), authen
+               // .getCredentials().toString().toCharArray());
+
+               return principals;
+       }
+
+       /**
+        * Super implementation removes all {@link Principal}, the Spring
+        * {@link org.springframework.security.Authentication} as well. Here we
+        * simply clear Jackrabbit related {@link Principal}s.
+        */
+       @Override
+       public boolean logout() throws LoginException {
+               clearPrincipals(AdminPrincipal.class);
+               clearPrincipals(ArgeoSystemPrincipal.class);
+               clearPrincipals(AnonymousPrincipal.class);
+               clearPrincipals(GrantedAuthorityPrincipal.class);
+
+               // we resync with Spring Security since the subject may have been reused
+               // in beetween
+               // TODO: check if this is clean
+               // subject.getPrincipals().addAll(syncPrincipals());
+
+               return true;
+       }
+
+       private <T extends Principal> void clearPrincipals(Class<T> clss) {
+               Set<T> principals = subject.getPrincipals(clss);
+               if (principals != null)
+                       principals.clear();
+       }
+
+       @SuppressWarnings("rawtypes")
+       @Override
+       protected void doInit(CallbackHandler callbackHandler, Session session,
+                       Map options) throws LoginException {
+       }
+
+       @Override
+       protected boolean impersonate(Principal principal, Credentials credentials)
+                       throws RepositoryException, LoginException {
+               throw new UnsupportedOperationException(
+                               "Impersonation is not yet supported");
+       }
+
+       @Override
+       protected Authentication getAuthentication(final Principal principal,
+                       Credentials creds) throws RepositoryException {
+               if (principal instanceof Group) {
+                       return null;
+               }
+               return new Authentication() {
+                       public boolean canHandle(Credentials credentials) {
+                               return principal instanceof org.springframework.security.Authentication;
+                       }
+
+                       public boolean authenticate(Credentials credentials)
+                                       throws RepositoryException {
+                               return ((org.springframework.security.Authentication) principal)
+                                               .isAuthenticated();
+                       }
+               };
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoSecurityManager.java b/trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoSecurityManager.java
new file mode 100644 (file)
index 0000000..3450c75
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * 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.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.security.auth.Subject;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+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.AnonymousPrincipal;
+import org.apache.jackrabbit.core.security.SecurityConstants;
+import org.apache.jackrabbit.core.security.authorization.WorkspaceAccessManager;
+import org.springframework.security.Authentication;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.context.SecurityContextHolder;
+
+/** Integrates Spring Security and Jackrabbit Security users and roles. */
+public class ArgeoSecurityManager extends DefaultSecurityManager {
+       /** Legacy security sync */
+       final static String PROPERTY_JACKRABBIT_SECURITY_SYNC_1_1 = "argeo.jackarabbit.securitySync.1.1";
+
+       private final static Log log = LogFactory
+                       .getLog(ArgeoSecurityManager.class);
+
+       private static Boolean synchronize = Boolean.parseBoolean(System
+                       .getProperty(PROPERTY_JACKRABBIT_SECURITY_SYNC_1_1, "false"));
+
+       /** TODO? use a bounded buffer */
+       private Map<String, String> userRolesCache = Collections
+                       .synchronizedMap(new HashMap<String, String>());
+
+       @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 {
+               if (!synchronize) {
+                       Authentication authentication = SecurityContextHolder.getContext()
+                                       .getAuthentication();
+                       if (authentication != null)
+                               return authentication.getName();
+                       else
+                               return super.getUserID(subject, workspaceName);
+               }
+
+               if (log.isTraceEnabled())
+                       log.trace(subject);
+               // skip anonymous user (no rights)
+               if (!subject.getPrincipals(AnonymousPrincipal.class).isEmpty())
+                       return super.getUserID(subject, workspaceName);
+               // skip Jackrabbit system user (all rights)
+               if (!subject.getPrincipals(ArgeoSystemPrincipal.class).isEmpty())
+                       return super.getUserID(subject, workspaceName);
+
+               // retrieve Spring authentication from JAAS
+               // TODO? use Spring Security context holder
+               Authentication authen;
+               Set<Authentication> authens = subject
+                               .getPrincipals(Authentication.class);
+               String userId = super.getUserID(subject, workspaceName);
+               if (authens.size() == 0) {
+                       // make sure that logged-in user has a Principal, useful for testing
+                       // using an admin user
+                       UserManager systemUm = getSystemUserManager(null);
+                       if (systemUm.getAuthorizable(userId) == null)
+                               systemUm.createUser(userId, "");
+               } else {// Spring Security
+                       authen = authens.iterator().next();
+
+                       if (!userId.equals(authen.getName()))
+                               log.warn("User ID is '" + userId + "' but authen is "
+                                               + authen.getName());
+                       StringBuffer roles = new StringBuffer("");
+                       GrantedAuthority[] authorities = authen.getAuthorities();
+                       for (GrantedAuthority ga : authorities) {
+                               roles.append(ga.toString());
+                       }
+
+                       // do not sync if not changed
+                       if (userRolesCache.containsKey(userId)
+                                       && userRolesCache.get(userId).equals(roles.toString()))
+                               return userId;
+
+                       // sync Spring and Jackrabbit
+                       // workspace is irrelevant here
+                       UserManager systemUm = getSystemUserManager(null);
+                       syncSpringAndJackrabbitSecurity(systemUm, authen);
+                       userRolesCache.put(userId, roles.toString());
+               }
+               return userId;
+       }
+
+       /**
+        * Make sure that the Jackrabbit security model contains this user and its
+        * granted authorities
+        */
+       static private void syncSpringAndJackrabbitSecurity(UserManager systemUm,
+                       Authentication authen) throws RepositoryException {
+               long begin = System.currentTimeMillis();
+
+               String userId = authen.getName();
+               User user = (User) systemUm.getAuthorizable(userId);
+               if (user == null) {
+                       user = systemUm.createUser(userId, authen.getCredentials()
+                                       .toString(), authen, null);
+                       log.info(userId + " added as " + user);
+               }
+
+               // process groups
+               List<String> userGroupIds = new ArrayList<String>();
+               for (GrantedAuthority ga : authen.getAuthorities()) {
+                       Group group = (Group) systemUm.getAuthorizable(ga.getAuthority());
+                       if (group == null) {
+                               group = systemUm.createGroup(ga.getAuthority());
+                               log.info(ga.getAuthority() + " added as " + group);
+                       }
+                       if (!group.isMember(user))
+                               group.addMember(user);
+                       userGroupIds.add(ga.getAuthority());
+               }
+
+               // check if user has not been removed from some groups
+               for (Iterator<Group> it = user.declaredMemberOf(); it.hasNext();) {
+                       Group group = it.next();
+                       if (!userGroupIds.contains(group.getID()))
+                               group.removeMember(user);
+               }
+
+               if (log.isTraceEnabled())
+                       log.trace("Spring and Jackrabbit Security synchronized for user "
+                                       + userId + " in " + (System.currentTimeMillis() - begin)
+                                       + " ms");
+       }
+
+       @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/trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoSystemPrincipal.java b/trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoSystemPrincipal.java
new file mode 100644 (file)
index 0000000..e38981e
--- /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 java.security.Principal;
+
+/** Principal for non-interactive system actions. */
+class ArgeoSystemPrincipal implements Principal {
+       private String name;
+
+       public ArgeoSystemPrincipal(String name) {
+               super();
+               this.name = name;
+       }
+
+       public String getName() {
+               return name;
+       }
+
+       @Override
+       public int hashCode() {
+               return getName().hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (!(obj instanceof ArgeoSystemPrincipal))
+                       return false;
+               return getName().equals(((ArgeoSystemPrincipal) obj).getName());
+       }
+
+       @Override
+       public String toString() {
+               return "Argeo System (non interactive) name=" + getName();
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/GrantedAuthorityPrincipal.java b/trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/GrantedAuthorityPrincipal.java
new file mode 100644 (file)
index 0000000..482214e
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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 org.springframework.security.GrantedAuthority;
+
+/** Wraps a {@link GrantedAuthority} as a principal. */
+class GrantedAuthorityPrincipal implements Principal {
+       private final GrantedAuthority grantedAuthority;
+
+       public GrantedAuthorityPrincipal(GrantedAuthority grantedAuthority) {
+               this.grantedAuthority = grantedAuthority;
+       }
+
+       public String getName() {
+               return grantedAuthority.getAuthority();
+       }
+
+       @Override
+       public int hashCode() {
+               return getName().hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (!(obj instanceof GrantedAuthorityPrincipal))
+                       return false;
+               return getName().equals(((GrantedAuthorityPrincipal) obj).getName());
+       }
+
+       @Override
+       public String toString() {
+               return "Granted Authority " + getName();
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/JackrabbitSecurityModel.java b/trunk/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/JackrabbitSecurityModel.java
new file mode 100644 (file)
index 0000000..a9985f9
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.security.jcr.SimpleJcrSecurityModel;
+
+/** Make sure that user authorizable exists before syncing user directories. */
+public class JackrabbitSecurityModel extends SimpleJcrSecurityModel {
+       private final static Log log = LogFactory
+                       .getLog(JackrabbitSecurityModel.class);
+
+       @Override
+       public synchronized Node sync(Session session, String username,
+                       List<String> roles) {
+               if (!(session instanceof JackrabbitSession))
+                       return super.sync(session, username, roles);
+
+               try {
+                       UserManager userManager = ((JackrabbitSession) session)
+                                       .getUserManager();
+                       User user = (User) userManager.getAuthorizable(username);
+                       if (user != null) {
+                               String principalName = user.getPrincipal().getName();
+                               if (!principalName.equals(username)) {
+                                       log.warn("Jackrabbit principal is '" + principalName
+                                                       + "' but username is '" + username
+                                                       + "'. Recreating...");
+                                       user.remove();
+                                       user = userManager.createUser(username, "");
+                               }
+                       } else {
+                               // create new principal
+                               user = userManager.createUser(username, "");
+                               log.info(username + " added as Jackrabbit user " + user);
+                       }
+
+                       // generic JCR sync
+                       Node userProfile = super.sync(session, username, roles);
+
+                       Boolean enabled = userProfile.getProperty(ArgeoNames.ARGEO_ENABLED)
+                                       .getBoolean();
+                       if (enabled && user.isDisabled())
+                               user.disable(null);
+                       else if (!enabled && !user.isDisabled())
+                               user.disable(userProfile.getPath() + " is disabled");
+
+                       // Sync Jackrabbit roles
+                       if (roles != null)
+                               syncRoles(userManager, user, roles);
+
+                       return userProfile;
+               } catch (RepositoryException e) {
+                       throw new ArgeoException(
+                                       "Cannot perform Jackrabbit specific operations", e);
+               }
+       }
+
+       /** Make sure Jackrabbit roles are in line with authentication */
+       void syncRoles(UserManager userManager, User user, List<String> roles)
+                       throws RepositoryException {
+               List<String> userGroupIds = new ArrayList<String>();
+               for (String role : roles) {
+                       Group group = (Group) userManager.getAuthorizable(role);
+                       if (group == null) {
+                               group = userManager.createGroup(role);
+                               log.info(role + " added as " + group);
+                       }
+                       if (!group.isMember(user))
+                               group.addMember(user);
+                       userGroupIds.add(role);
+               }
+
+               // check if user has not been removed from some groups
+               for (Iterator<Group> it = user.declaredMemberOf(); it.hasNext();) {
+                       Group group = it.next();
+                       if (!userGroupIds.contains(group.getID()))
+                               group.removeMember(user);
+               }
+       }
+}
diff --git a/trunk/security/runtime/org.argeo.security.ldap/.classpath b/trunk/security/runtime/org.argeo.security.ldap/.classpath
new file mode 100644 (file)
index 0000000..5641c7c
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/security/runtime/org.argeo.security.ldap/.project b/trunk/security/runtime/org.argeo.security.ldap/.project
new file mode 100644 (file)
index 0000000..942f140
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.security.ldap</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/trunk/security/runtime/org.argeo.security.ldap/build.properties b/trunk/security/runtime/org.argeo.security.ldap/build.properties
new file mode 100644 (file)
index 0000000..5fc538b
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .
diff --git a/trunk/security/runtime/org.argeo.security.ldap/pom.xml b/trunk/security/runtime/org.argeo.security.ldap/pom.xml
new file mode 100644 (file)
index 0000000..75ad8b6
--- /dev/null
@@ -0,0 +1,76 @@
+<?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.security</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.ldap</artifactId>
+       <name>Commons Security LDAP</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>
+                                                       org.argeo.security.ldap.*
+                                               </Export-Package>
+                                               <Import-Package>
+                                                       org.springframework.core,
+                                                       org.springframework.dao,
+                                                       javax.jcr.nodetype,
+                                                       *
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.core</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- JCR -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.jcr</artifactId>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.ldap</artifactId>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/ArgeoLdapShaPasswordEncoder.java b/trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/ArgeoLdapShaPasswordEncoder.java
new file mode 100644 (file)
index 0000000..ea22ef3
--- /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.ldap;
+
+import org.springframework.security.providers.ldap.authenticator.LdapShaPasswordEncoder;
+
+/**
+ * {@link LdapShaPasswordEncoder} allowing to configure the usage of salt (APache
+ * Directory Server 1.0 does not support bind with SSHA)
+ */
+public class ArgeoLdapShaPasswordEncoder extends LdapShaPasswordEncoder {
+       private Boolean useSalt = true;
+
+       @Override
+       public String encodePassword(String rawPass, Object salt) {
+               return super.encodePassword(rawPass, useSalt ? salt : null);
+       }
+
+       public void setUseSalt(Boolean useSalt) {
+               this.useSalt = useSalt;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/ArgeoLdapUserDetailsManager.java b/trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/ArgeoLdapUserDetailsManager.java
new file mode 100644 (file)
index 0000000..0c7368f
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * 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.ldap;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.argeo.ArgeoException;
+import org.argeo.security.UserAdminService;
+import org.springframework.ldap.core.ContextSource;
+import org.springframework.security.Authentication;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.providers.encoding.PasswordEncoder;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.ldap.LdapUserDetailsManager;
+
+/** Extends {@link LdapUserDetailsManager} by adding password encoding support. */
+public class ArgeoLdapUserDetailsManager extends LdapUserDetailsManager
+               implements UserAdminService {
+       private String superUsername = "root";
+       private ArgeoUserAdminDaoLdap userAdminDao;
+       private PasswordEncoder passwordEncoder;
+       private final Random random;
+
+       public ArgeoLdapUserDetailsManager(ContextSource contextSource) {
+               super(contextSource);
+               this.random = createRandom();
+       }
+
+       private static Random createRandom() {
+               try {
+                       return SecureRandom.getInstance("SHA1PRNG");
+               } catch (NoSuchAlgorithmException e) {
+                       return new Random(System.currentTimeMillis());
+               }
+       }
+
+       @Override
+       public void changePassword(String oldPassword, String newPassword) {
+               Authentication authentication = SecurityContextHolder.getContext()
+                               .getAuthentication();
+               if (authentication == null)
+                       throw new ArgeoException(
+                                       "Cannot change password without authentication");
+               String username = authentication.getName();
+               UserDetails userDetails = loadUserByUsername(username);
+               String currentPassword = userDetails.getPassword();
+               if (currentPassword == null)
+                       throw new ArgeoException("Cannot access current password");
+               if (!passwordEncoder
+                               .isPasswordValid(currentPassword, oldPassword, null))
+                       throw new ArgeoException("Old password invalid");
+               // Spring Security LDAP 2.0 is buggy when used with OpenLDAP and called
+               // with oldPassword argument
+               super.changePassword(null, encodePassword(newPassword));
+       }
+
+       public void newRole(String role) {
+               userAdminDao.createRole(role, superUsername);
+       }
+
+       public void synchronize() {
+               for (String username : userAdminDao.listUsers())
+                       loadUserByUsername(username);
+               // TODO: find a way to remove from JCR
+       }
+
+       public void deleteRole(String role) {
+               userAdminDao.deleteRole(role);
+       }
+
+       public Set<String> listUsers() {
+               return userAdminDao.listUsers();
+       }
+
+       public Set<String> listUsersInRole(String role) {
+               Set<String> lst = new TreeSet<String>(
+                               userAdminDao.listUsersInRole(role));
+               Iterator<String> it = lst.iterator();
+               while (it.hasNext()) {
+                       if (it.next().equals(superUsername)) {
+                               it.remove();
+                               break;
+                       }
+               }
+               return lst;
+       }
+
+       public List<String> listUserRoles(String username) {
+               UserDetails userDetails = loadUserByUsername(username);
+               List<String> roles = new ArrayList<String>();
+               for (GrantedAuthority ga : userDetails.getAuthorities()) {
+                       roles.add(ga.getAuthority());
+               }
+               return Collections.unmodifiableList(roles);
+       }
+
+       public Set<String> listEditableRoles() {
+               return userAdminDao.listEditableRoles();
+       }
+
+       protected String encodePassword(String password) {
+               if (!password.startsWith("{")) {
+                       byte[] salt = new byte[16];
+                       random.nextBytes(salt);
+                       return passwordEncoder.encodePassword(password, salt);
+               } else {
+                       return password;
+               }
+       }
+
+       public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
+               this.passwordEncoder = passwordEncoder;
+       }
+
+       public void setSuperUsername(String superUsername) {
+               this.superUsername = superUsername;
+       }
+
+       public void setUserAdminDao(ArgeoUserAdminDaoLdap userAdminDao) {
+               this.userAdminDao = userAdminDao;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/ArgeoUserAdminDaoLdap.java b/trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/ArgeoUserAdminDaoLdap.java
new file mode 100644 (file)
index 0000000..37d2a06
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * 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.ldap;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+
+import org.springframework.ldap.core.ContextExecutor;
+import org.springframework.ldap.core.ContextMapper;
+import org.springframework.ldap.core.DirContextAdapter;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.core.support.BaseLdapPathContextSource;
+import org.springframework.security.ldap.LdapUsernameToDnMapper;
+import org.springframework.security.ldap.LdapUtils;
+
+/**
+ * Wraps low-level LDAP operation on user and roles, used by
+ * {@link ArgeoLdapUserDetailsManager}
+ */
+public class ArgeoUserAdminDaoLdap {
+       private String userBase;
+       private String usernameAttribute;
+       private String groupBase;
+       private String[] groupClasses;
+
+       private String groupRoleAttribute;
+       private String groupMemberAttribute;
+       private String defaultRole;
+       private String rolePrefix;
+
+       private final LdapTemplate ldapTemplate;
+       private LdapUsernameToDnMapper usernameMapper;
+
+       /**
+        * Standard constructor, using the LDAP context source shared with Spring
+        * Security components.
+        */
+       public ArgeoUserAdminDaoLdap(BaseLdapPathContextSource contextSource) {
+               this.ldapTemplate = new LdapTemplate(contextSource);
+       }
+
+       @SuppressWarnings("unchecked")
+       public synchronized Set<String> listUsers() {
+               List<String> usernames = (List<String>) ldapTemplate.listBindings(
+                               new DistinguishedName(userBase), new ContextMapper() {
+                                       public Object mapFromContext(Object ctxArg) {
+                                               DirContextAdapter ctx = (DirContextAdapter) ctxArg;
+                                               return ctx.getStringAttribute(usernameAttribute);
+                                       }
+                               });
+
+               return Collections
+                               .unmodifiableSortedSet(new TreeSet<String>(usernames));
+       }
+
+       @SuppressWarnings("unchecked")
+       public Set<String> listEditableRoles() {
+               return Collections.unmodifiableSortedSet(new TreeSet<String>(
+                               ldapTemplate.listBindings(groupBase, new ContextMapper() {
+                                       public Object mapFromContext(Object ctxArg) {
+                                               String groupName = ((DirContextAdapter) ctxArg)
+                                                               .getStringAttribute(groupRoleAttribute);
+                                               String roleName = convertGroupToRole(groupName);
+                                               return roleName;
+                                       }
+                               })));
+       }
+
+       @SuppressWarnings("unchecked")
+       public Set<String> listUsersInRole(String role) {
+               return (Set<String>) ldapTemplate.lookup(
+                               buildGroupDn(convertRoleToGroup(role)), new ContextMapper() {
+                                       public Object mapFromContext(Object ctxArg) {
+                                               DirContextAdapter ctx = (DirContextAdapter) ctxArg;
+                                               String[] userDns = ctx
+                                                               .getStringAttributes(groupMemberAttribute);
+                                               TreeSet<String> set = new TreeSet<String>();
+                                               for (String userDn : userDns) {
+                                                       DistinguishedName dn = new DistinguishedName(userDn);
+                                                       String username = dn.getValue(usernameAttribute);
+                                                       set.add(username);
+                                               }
+                                               return Collections.unmodifiableSortedSet(set);
+                                       }
+                               });
+       }
+
+       public void createRole(String role, final String superuserName) {
+               String group = convertRoleToGroup(role);
+               DistinguishedName superuserDn = (DistinguishedName) ldapTemplate
+                               .executeReadWrite(new ContextExecutor() {
+                                       public Object executeWithContext(DirContext ctx)
+                                                       throws NamingException {
+                                               return LdapUtils.getFullDn(
+                                                               usernameMapper.buildDn(superuserName), ctx);
+                                       }
+                               });
+
+               Name groupDn = buildGroupDn(group);
+               DirContextAdapter context = new DirContextAdapter();
+               context.setAttributeValues("objectClass", groupClasses);
+               context.setAttributeValue("cn", group);
+               // Add superuser because cannot create empty group
+               context.setAttributeValue(groupMemberAttribute, superuserDn.toString());
+               ldapTemplate.bind(groupDn, context, null);
+       }
+
+       public void deleteRole(String role) {
+               String group = convertRoleToGroup(role);
+               Name dn = buildGroupDn(group);
+               ldapTemplate.unbind(dn);
+       }
+
+       /** Maps a role (ROLE_XXX) to the related LDAP group (xxx) */
+       protected String convertRoleToGroup(String role) {
+               String group = role;
+               if (group.startsWith(rolePrefix)) {
+                       group = group.substring(rolePrefix.length());
+                       group = group.toLowerCase();
+               }
+               return group;
+       }
+
+       /** Maps anLDAP group (xxx) to the related role (ROLE_XXX) */
+       protected String convertGroupToRole(String groupName) {
+               groupName = groupName.toUpperCase();
+
+               return rolePrefix + groupName;
+       }
+
+       protected Name buildGroupDn(String name) {
+               return new DistinguishedName(groupRoleAttribute + "=" + name + ","
+                               + groupBase);
+       }
+
+       public void setUserBase(String userBase) {
+               this.userBase = userBase;
+       }
+
+       public void setUsernameAttribute(String usernameAttribute) {
+               this.usernameAttribute = usernameAttribute;
+       }
+
+       public void setGroupBase(String groupBase) {
+               this.groupBase = groupBase;
+       }
+
+       public void setGroupRoleAttribute(String groupRoleAttributeName) {
+               this.groupRoleAttribute = groupRoleAttributeName;
+       }
+
+       public void setGroupMemberAttribute(String groupMemberAttributeName) {
+               this.groupMemberAttribute = groupMemberAttributeName;
+       }
+
+       public void setDefaultRole(String defaultRole) {
+               this.defaultRole = defaultRole;
+       }
+
+       public void setRolePrefix(String rolePrefix) {
+               this.rolePrefix = rolePrefix;
+       }
+
+       public void setUsernameMapper(LdapUsernameToDnMapper usernameMapper) {
+               this.usernameMapper = usernameMapper;
+       }
+
+       public String getDefaultRole() {
+               return defaultRole;
+       }
+
+       public void setGroupClasses(String[] groupClasses) {
+               this.groupClasses = groupClasses;
+       }
+}
diff --git a/trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrLdapSynchronizer.java b/trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrLdapSynchronizer.java
new file mode 100644 (file)
index 0000000..3e9e2cb
--- /dev/null
@@ -0,0 +1,619 @@
+/*
+ * 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.ldap.jcr;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.SortedSet;
+import java.util.UUID;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.query.Query;
+import javax.jcr.version.VersionManager;
+import javax.naming.Name;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.security.SecurityUtils;
+import org.argeo.security.jcr.JcrSecurityModel;
+import org.argeo.security.jcr.JcrUserDetails;
+import org.argeo.security.jcr.SimpleJcrSecurityModel;
+import org.springframework.ldap.core.ContextMapper;
+import org.springframework.ldap.core.DirContextAdapter;
+import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.ldap.LdapUsernameToDnMapper;
+import org.springframework.security.providers.encoding.PasswordEncoder;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.ldap.UserDetailsContextMapper;
+
+/** Makes sure that LDAP and JCR are in line. */
+public class JcrLdapSynchronizer implements UserDetailsContextMapper,
+               ArgeoNames {
+       private final static Log log = LogFactory.getLog(JcrLdapSynchronizer.class);
+
+       // LDAP
+       private LdapTemplate ldapTemplate;
+       /**
+        * LDAP template whose context source has an object factory set to null. see
+        * <a href=
+        * "http://forum.springsource.org/showthread.php?55955-Persistent-search-with-spring-ldap"
+        * >this</a>
+        */
+       // private LdapTemplate rawLdapTemplate;
+
+       private String userBase;
+       private String usernameAttribute;
+       private String passwordAttribute;
+       private String[] userClasses;
+       // private String defaultUserRole ="ROLE_USER";
+
+       // private NamingListener ldapUserListener;
+       // private SearchControls subTreeSearchControls;
+       private LdapUsernameToDnMapper usernameMapper;
+
+       private PasswordEncoder passwordEncoder;
+       private final Random random;
+
+       // JCR
+       /** Admin session on the main workspace */
+       private Session nodeSession;
+       private Repository repository;
+
+       // private JcrProfileListener jcrProfileListener;
+       private JcrSecurityModel jcrSecurityModel = new SimpleJcrSecurityModel();
+
+       // Mapping
+       private Map<String, String> propertyToAttributes = new HashMap<String, String>();
+
+       public JcrLdapSynchronizer() {
+               random = createRandom();
+       }
+
+       public void init() {
+               try {
+                       nodeSession = repository.login();
+
+                       // TODO put this in a different thread, and poll the LDAP server
+                       // until it is up
+                       try {
+                               synchronize();
+
+                               // LDAP
+                               // subTreeSearchControls = new SearchControls();
+                               // subTreeSearchControls
+                               // .setSearchScope(SearchControls.SUBTREE_SCOPE);
+                               // LDAP listener
+                               // ldapUserListener = new LdapUserListener();
+                               // rawLdapTemplate.executeReadOnly(new ContextExecutor() {
+                               // public Object executeWithContext(DirContext ctx)
+                               // throws NamingException {
+                               // EventDirContext ectx = (EventDirContext) ctx.lookup("");
+                               // ectx.addNamingListener(userBase, "("
+                               // + usernameAttribute + "=*)",
+                               // subTreeSearchControls, ldapUserListener);
+                               // return null;
+                               // }
+                               // });
+                       } catch (Exception e) {
+                               log.error("Could not synchronize and listen to LDAP,"
+                                               + " probably because the LDAP server is not available."
+                                               + " Restart the system as soon as possible.", e);
+                       }
+
+                       // JCR
+                       // String[] nodeTypes = { ArgeoTypes.ARGEO_USER_PROFILE };
+                       // jcrProfileListener = new JcrProfileListener();
+                       // noLocal is used so that we are not notified when we modify JCR
+                       // from LDAP
+                       // nodeSession
+                       // .getWorkspace()
+                       // .getObservationManager()
+                       // .addEventListener(jcrProfileListener,
+                       // Event.PROPERTY_CHANGED | Event.NODE_ADDED, "/",
+                       // true, null, nodeTypes, true);
+               } catch (Exception e) {
+                       JcrUtils.logoutQuietly(nodeSession);
+                       throw new ArgeoException("Cannot initialize LDAP/JCR synchronizer",
+                                       e);
+               }
+       }
+
+       public void destroy() {
+               // JcrUtils.removeListenerQuietly(nodeSession, jcrProfileListener);
+               JcrUtils.logoutQuietly(nodeSession);
+               // try {
+               // rawLdapTemplate.executeReadOnly(new ContextExecutor() {
+               // public Object executeWithContext(DirContext ctx)
+               // throws NamingException {
+               // EventDirContext ectx = (EventDirContext) ctx.lookup("");
+               // ectx.removeNamingListener(ldapUserListener);
+               // return null;
+               // }
+               // });
+               // } catch (Exception e) {
+               // // silent (LDAP server may have been shutdown already)
+               // if (log.isTraceEnabled())
+               // log.trace("Cannot remove LDAP listener", e);
+               // }
+       }
+
+       /*
+        * LDAP TO JCR
+        */
+       /** Full synchronization between LDAP and JCR. LDAP has priority. */
+       protected void synchronize() {
+               try {
+                       Name userBaseName = new DistinguishedName(userBase);
+                       // TODO subtree search?
+                       @SuppressWarnings("unchecked")
+                       List<String> userPaths = (List<String>) ldapTemplate.listBindings(
+                                       userBaseName, new ContextMapper() {
+                                               public Object mapFromContext(Object ctxObj) {
+                                                       try {
+                                                               return mapLdapToJcr((DirContextAdapter) ctxObj);
+                                                       } catch (Exception e) {
+                                                               // do not break process because of error
+                                                               log.error(
+                                                                               "Could not LDAP->JCR synchronize user "
+                                                                                               + ctxObj, e);
+                                                               return null;
+                                                       }
+                                               }
+                                       });
+
+                       // create accounts which are not in LDAP
+                       Query query = nodeSession
+                                       .getWorkspace()
+                                       .getQueryManager()
+                                       .createQuery(
+                                                       "select * from [" + ArgeoTypes.ARGEO_USER_PROFILE
+                                                                       + "]", Query.JCR_SQL2);
+                       NodeIterator it = query.execute().getNodes();
+                       while (it.hasNext()) {
+                               Node userProfile = it.nextNode();
+                               String path = userProfile.getPath();
+                               try {
+                                       if (!userPaths.contains(path)) {
+                                               String username = userProfile
+                                                               .getProperty(ARGEO_USER_ID).getString();
+                                               // GrantedAuthority[] authorities = {new
+                                               // GrantedAuthorityImpl(defaultUserRole)};
+                                               GrantedAuthority[] authorities = {};
+                                               JcrUserDetails userDetails = new JcrUserDetails(
+                                                               userProfile, username, authorities);
+                                               String dn = createLdapUser(userDetails);
+                                               log.warn("Created ldap entry '" + dn + "' for user '"
+                                                               + username + "'");
+
+                                               // if(!userProfile.getProperty(ARGEO_ENABLED).getBoolean()){
+                                               // continue profiles;
+                                               // }
+                                               //
+                                               // log.warn("Path "
+                                               // + path
+                                               // + " not found in LDAP, disabling user "
+                                               // + userProfile.getProperty(ArgeoNames.ARGEO_USER_ID)
+                                               // .getString());
+
+                                               // Temporary hack to repair previous behaviour
+                                               if (!userProfile.getProperty(ARGEO_ENABLED)
+                                                               .getBoolean()) {
+                                                       VersionManager versionManager = nodeSession
+                                                                       .getWorkspace().getVersionManager();
+                                                       versionManager.checkout(userProfile.getPath());
+                                                       userProfile.setProperty(ArgeoNames.ARGEO_ENABLED,
+                                                                       true);
+                                                       nodeSession.save();
+                                                       versionManager.checkin(userProfile.getPath());
+                                               }
+                                       }
+                               } catch (Exception e) {
+                                       log.error("Cannot process " + path, e);
+                               }
+                       }
+               } catch (Exception e) {
+                       JcrUtils.discardQuietly(nodeSession);
+                       log.error("Cannot synchronize LDAP and JCR", e);
+                       // throw new ArgeoException("Cannot synchronize LDAP and JCR", e);
+               }
+       }
+
+       private String createLdapUser(UserDetails user) {
+               DirContextAdapter ctx = new DirContextAdapter();
+               mapUserToContext(user, ctx);
+               DistinguishedName dn = usernameMapper.buildDn(user.getUsername());
+               ldapTemplate.bind(dn, ctx, null);
+               return dn.toString();
+       }
+
+       /** Called during authentication in order to retrieve user details */
+       public UserDetails mapUserFromContext(final DirContextOperations ctx,
+                       final String username, GrantedAuthority[] authorities) {
+               if (ctx == null)
+                       throw new ArgeoException("No LDAP information for user " + username);
+
+               String ldapUsername = ctx.getStringAttribute(usernameAttribute);
+               if (!ldapUsername.equals(username))
+                       throw new ArgeoException("Logged in with username " + username
+                                       + " but LDAP user is " + ldapUsername);
+
+               Node userProfile = jcrSecurityModel.sync(nodeSession, username,
+                               SecurityUtils.authoritiesToStringList(authorities));
+               // JcrUserDetails.checkAccountStatus(userProfile);
+
+               // password
+               SortedSet<?> passwordAttributes = ctx
+                               .getAttributeSortedStringSet(passwordAttribute);
+               String password;
+               if (passwordAttributes == null || passwordAttributes.size() == 0) {
+                       //throw new ArgeoException("No password found for user " + username);
+                       password = "NULL";
+               } else {
+                       byte[] arr = (byte[]) passwordAttributes.first();
+                       password = new String(arr);
+                       // erase password
+                       Arrays.fill(arr, (byte) 0);
+               }
+
+               try {
+                       return new JcrUserDetails(userProfile, password, authorities);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot retrieve user details for "
+                                       + username, e);
+               }
+       }
+
+       /**
+        * Writes an LDAP context to the JCR user profile.
+        * 
+        * @return path to user profile
+        */
+       protected synchronized String mapLdapToJcr(DirContextAdapter ctx) {
+               Session session = nodeSession;
+               try {
+                       // process
+                       String username = ctx.getStringAttribute(usernameAttribute);
+
+                       Node userProfile = jcrSecurityModel.sync(session, username, null);
+                       Map<String, String> modifications = new HashMap<String, String>();
+                       for (String jcrProperty : propertyToAttributes.keySet())
+                               ldapToJcr(userProfile, jcrProperty, ctx, modifications);
+
+                       int modifCount = modifications.size();
+                       if (modifCount > 0) {
+                               session.getWorkspace().getVersionManager()
+                                               .checkout(userProfile.getPath());
+                               for (String prop : modifications.keySet())
+                                       userProfile.setProperty(prop, modifications.get(prop));
+                               JcrUtils.updateLastModified(userProfile);
+                               session.save();
+                               session.getWorkspace().getVersionManager()
+                                               .checkin(userProfile.getPath());
+                               if (log.isDebugEnabled())
+                                       log.debug("Mapped " + modifCount + " LDAP modification"
+                                                       + (modifCount == 1 ? "" : "s") + " from "
+                                                       + ctx.getDn() + " to " + userProfile);
+                       }
+                       return userProfile.getPath();
+               } catch (Exception e) {
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoException("Cannot synchronize JCR and LDAP", e);
+               }
+       }
+
+       /** Maps an LDAP property to a JCR property */
+       protected void ldapToJcr(Node userProfile, String jcrProperty,
+                       DirContextOperations ctx, Map<String, String> modifications) {
+               // TODO do we really need DirContextOperations?
+               try {
+                       String ldapAttribute;
+                       if (propertyToAttributes.containsKey(jcrProperty))
+                               ldapAttribute = propertyToAttributes.get(jcrProperty);
+                       else
+                               throw new ArgeoException(
+                                               "No LDAP attribute mapped for JCR proprty "
+                                                               + jcrProperty);
+
+                       String value = ctx.getStringAttribute(ldapAttribute);
+                       String jcrValue = userProfile.hasProperty(jcrProperty) ? userProfile
+                                       .getProperty(jcrProperty).getString() : null;
+                       if (value != null && jcrValue != null) {
+                               if (!value.equals(jcrValue))
+                                       modifications.put(jcrProperty, value);
+                       } else if (value != null && jcrValue == null) {
+                               modifications.put(jcrProperty, value);
+                       } else if (value == null && jcrValue != null) {
+                               modifications.put(jcrProperty, value);
+                       }
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot map JCR property " + jcrProperty
+                                       + " from LDAP", e);
+               }
+       }
+
+       /*
+        * JCR to LDAP
+        */
+
+       public void mapUserToContext(UserDetails user, final DirContextAdapter ctx) {
+               if (!(user instanceof JcrUserDetails))
+                       throw new ArgeoException("Unsupported user details: "
+                                       + user.getClass());
+
+               ctx.setAttributeValues("objectClass", userClasses);
+               ctx.setAttributeValue(usernameAttribute, user.getUsername());
+               ctx.setAttributeValue(passwordAttribute,
+                               encodePassword(user.getPassword()));
+
+               final JcrUserDetails jcrUserDetails = (JcrUserDetails) user;
+               try {
+                       Node userProfile = nodeSession
+                                       .getNode(jcrUserDetails.getHomePath()).getNode(
+                                                       ARGEO_PROFILE);
+                       for (String jcrProperty : propertyToAttributes.keySet()) {
+                               if (userProfile.hasProperty(jcrProperty)) {
+                                       ModificationItem mi = jcrToLdap(jcrProperty, userProfile
+                                                       .getProperty(jcrProperty).getString());
+                                       if (mi != null)
+                                               ctx.setAttribute(mi.getAttribute());
+                               }
+                       }
+                       if (log.isTraceEnabled())
+                               log.trace("Mapped " + userProfile + " to " + ctx.getDn());
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot synchronize JCR and LDAP", e);
+               }
+
+       }
+
+       /** Maps a JCR property to an LDAP property */
+       protected ModificationItem jcrToLdap(String jcrProperty, String value) {
+               // TODO do we really need DirContextOperations?
+               try {
+                       String ldapAttribute;
+                       if (propertyToAttributes.containsKey(jcrProperty))
+                               ldapAttribute = propertyToAttributes.get(jcrProperty);
+                       else
+                               return null;
+
+                       // fix issue with empty 'sn' in LDAP
+                       if (ldapAttribute.equals("sn") && (value.trim().equals("")))
+                               return null;
+                       // fix issue with empty 'description' in LDAP
+                       if (ldapAttribute.equals("description") && value.trim().equals(""))
+                               return null;
+                       BasicAttribute attr = new BasicAttribute(
+                                       propertyToAttributes.get(jcrProperty), value);
+                       ModificationItem mi = new ModificationItem(
+                                       DirContext.REPLACE_ATTRIBUTE, attr);
+                       return mi;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot map JCR property " + jcrProperty
+                                       + " from LDAP", e);
+               }
+       }
+
+       /*
+        * UTILITIES
+        */
+       protected String encodePassword(String password) {
+               if (!password.startsWith("{")) {
+                       byte[] salt = new byte[16];
+                       random.nextBytes(salt);
+                       return passwordEncoder.encodePassword(password, salt);
+               } else {
+                       return password;
+               }
+       }
+
+       private static Random createRandom() {
+               try {
+                       return SecureRandom.getInstance("SHA1PRNG");
+               } catch (NoSuchAlgorithmException e) {
+                       return new Random(System.currentTimeMillis());
+               }
+       }
+
+       /*
+        * DEPENDENCY INJECTION
+        */
+
+       public void setLdapTemplate(LdapTemplate ldapTemplate) {
+               this.ldapTemplate = ldapTemplate;
+       }
+
+       public void setRawLdapTemplate(LdapTemplate rawLdapTemplate) {
+               // this.rawLdapTemplate = rawLdapTemplate;
+       }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setUserBase(String userBase) {
+               this.userBase = userBase;
+       }
+
+       public void setUsernameAttribute(String usernameAttribute) {
+               this.usernameAttribute = usernameAttribute;
+       }
+
+       public void setPropertyToAttributes(Map<String, String> propertyToAttributes) {
+               this.propertyToAttributes = propertyToAttributes;
+       }
+
+       public void setUsernameMapper(LdapUsernameToDnMapper usernameMapper) {
+               this.usernameMapper = usernameMapper;
+       }
+
+       public void setPasswordAttribute(String passwordAttribute) {
+               this.passwordAttribute = passwordAttribute;
+       }
+
+       public void setUserClasses(String[] userClasses) {
+               this.userClasses = userClasses;
+       }
+
+       public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
+               this.passwordEncoder = passwordEncoder;
+       }
+
+       public void setJcrSecurityModel(JcrSecurityModel jcrSecurityModel) {
+               this.jcrSecurityModel = jcrSecurityModel;
+       }
+
+       /** Listen to LDAP */
+       // class LdapUserListener implements ObjectChangeListener,
+       // NamespaceChangeListener, UnsolicitedNotificationListener {
+       //
+       // public void namingExceptionThrown(NamingExceptionEvent evt) {
+       // evt.getException().printStackTrace();
+       // }
+       //
+       // public void objectChanged(NamingEvent evt) {
+       // Binding user = evt.getNewBinding();
+       // // TODO find a way not to be called when JCR is the source of the
+       // // modification
+       // DirContextAdapter ctx = (DirContextAdapter) ldapTemplate
+       // .lookup(user.getName());
+       // mapLdapToJcr(ctx);
+       // }
+       //
+       // public void objectAdded(NamingEvent evt) {
+       // Binding user = evt.getNewBinding();
+       // DirContextAdapter ctx = (DirContextAdapter) ldapTemplate
+       // .lookup(user.getName());
+       // mapLdapToJcr(ctx);
+       // }
+       //
+       // public void objectRemoved(NamingEvent evt) {
+       // if (log.isDebugEnabled())
+       // log.debug(evt);
+       // }
+       //
+       // public void objectRenamed(NamingEvent evt) {
+       // if (log.isDebugEnabled())
+       // log.debug(evt);
+       // }
+       //
+       // public void notificationReceived(UnsolicitedNotificationEvent evt) {
+       // UnsolicitedNotification notification = evt.getNotification();
+       // NamingException ne = notification.getException();
+       // String msg = "LDAP notification " + "ID=" + notification.getID()
+       // + ", referrals=" + notification.getReferrals();
+       // if (ne != null) {
+       // if (log.isTraceEnabled())
+       // log.trace(msg + ", exception= " + ne, ne);
+       // else
+       // log.warn(msg + ", exception= " + ne);
+       // } else if (log.isDebugEnabled()) {
+       // log.debug("Unsollicited LDAP notification " + msg);
+       // }
+       // }
+       //
+       // }
+
+       /** Listen to JCR */
+       // class JcrProfileListener implements EventListener {
+       //
+       // public void onEvent(EventIterator events) {
+       // try {
+       // final Map<Name, List<ModificationItem>> modifications = new HashMap<Name,
+       // List<ModificationItem>>();
+       // while (events.hasNext()) {
+       // Event event = events.nextEvent();
+       // try {
+       // if (Event.PROPERTY_CHANGED == event.getType()) {
+       // Property property = (Property) nodeSession
+       // .getItem(event.getPath());
+       // String propertyName = property.getName();
+       // Node userProfile = property.getParent();
+       // String username = userProfile.getProperty(
+       // ARGEO_USER_ID).getString();
+       // if (propertyToAttributes.containsKey(propertyName)) {
+       // Name name = usernameMapper.buildDn(username);
+       // if (!modifications.containsKey(name))
+       // modifications.put(name,
+       // new ArrayList<ModificationItem>());
+       // String value = property.getString();
+       // ModificationItem mi = jcrToLdap(propertyName,
+       // value);
+       // if (mi != null)
+       // modifications.get(name).add(mi);
+       // }
+       // } else if (Event.NODE_ADDED == event.getType()) {
+       // Node userProfile = nodeSession.getNode(event
+       // .getPath());
+       // String username = userProfile.getProperty(
+       // ARGEO_USER_ID).getString();
+       // Name name = usernameMapper.buildDn(username);
+       // for (String propertyName : propertyToAttributes
+       // .keySet()) {
+       // if (!modifications.containsKey(name))
+       // modifications.put(name,
+       // new ArrayList<ModificationItem>());
+       // String value = userProfile.getProperty(
+       // propertyName).getString();
+       // ModificationItem mi = jcrToLdap(propertyName,
+       // value);
+       // if (mi != null)
+       // modifications.get(name).add(mi);
+       // }
+       // }
+       // } catch (RepositoryException e) {
+       // throw new ArgeoException("Cannot process event "
+       // + event, e);
+       // }
+       // }
+       //
+       // for (Name name : modifications.keySet()) {
+       // List<ModificationItem> userModifs = modifications.get(name);
+       // int modifCount = userModifs.size();
+       // ldapTemplate.modifyAttributes(name, userModifs
+       // .toArray(new ModificationItem[modifCount]));
+       // if (log.isDebugEnabled())
+       // log.debug("Mapped " + modifCount + " JCR modification"
+       // + (modifCount == 1 ? "" : "s") + " to " + name);
+       // }
+       // } catch (Exception e) {
+       // // if (log.isDebugEnabled())
+       // // e.printStackTrace();
+       // throw new ArgeoException("Cannot process JCR events ("
+       // + e.getMessage() + ")", e);
+       // }
+       // }
+       //
+       // }
+}
diff --git a/trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java b/trunk/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java
new file mode 100644 (file)
index 0000000..87973d9
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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.ldap.jcr;
+
+import java.util.UUID;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.security.jcr.JcrUserDetails;
+import org.springframework.ldap.core.DirContextAdapter;
+import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.ldap.UserDetailsContextMapper;
+
+/** @deprecated Read only mapping from LDAP to user details */
+@Deprecated
+public class JcrUserDetailsContextMapper implements UserDetailsContextMapper,
+               ArgeoNames {
+       /** Admin session on the security workspace */
+       private Session securitySession;
+       private Repository repository;
+       private String securityWorkspace = "security";
+
+       public void init() {
+               try {
+                       securitySession = repository.login(securityWorkspace);
+               } catch (RepositoryException e) {
+                       JcrUtils.logoutQuietly(securitySession);
+                       throw new ArgeoException(
+                                       "Cannot initialize LDAP/JCR user details context mapper", e);
+               }
+       }
+
+       public void destroy() {
+               JcrUtils.logoutQuietly(securitySession);
+       }
+
+       /** Called during authentication in order to retrieve user details */
+       public UserDetails mapUserFromContext(final DirContextOperations ctx,
+                       final String username, GrantedAuthority[] authorities) {
+               if (ctx == null)
+                       throw new ArgeoException("No LDAP information for user " + username);
+               Node userHome = UserJcrUtils.getUserHome(securitySession, username);
+               if (userHome == null)
+                       throw new ArgeoException("No JCR information for user " + username);
+
+               // password
+               // SortedSet<?> passwordAttributes = ctx
+               // .getAttributeSortedStringSet(passwordAttribute);
+               // String password;
+               // if (passwordAttributes == null || passwordAttributes.size() == 0) {
+               // throw new ArgeoException("No password found for user " + username);
+               // } else {
+               // byte[] arr = (byte[]) passwordAttributes.first();
+               // password = new String(arr);
+               // // erase password
+               // Arrays.fill(arr, (byte) 0);
+               // }
+
+               try {
+                       // we don't have access to password, so let's not pretend
+                       String password = UUID.randomUUID().toString();
+                       return new JcrUserDetails(userHome.getNode(ARGEO_PROFILE),
+                                       password, authorities);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot retrieve user details for "
+                                       + username, e);
+               }
+       }
+
+       public void mapUserToContext(UserDetails user, final DirContextAdapter ctx) {
+               throw new UnsupportedOperationException("LDAP access is read-only");
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.mvc/.classpath b/trunk/security/runtime/org.argeo.security.mvc/.classpath
new file mode 100644 (file)
index 0000000..ff41fbb
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/trunk/security/runtime/org.argeo.security.mvc/.project b/trunk/security/runtime/org.argeo.security.mvc/.project
new file mode 100644 (file)
index 0000000..b287d08
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.security.mvc</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/trunk/security/runtime/org.argeo.security.mvc/.settings/org.eclipse.jdt.core.prefs b/trunk/security/runtime/org.argeo.security.mvc/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..d2110d0
--- /dev/null
@@ -0,0 +1,5 @@
+#Tue Sep 15 11:35:07 CEST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/security/runtime/org.argeo.security.mvc/.settings/org.maven.ide.eclipse.prefs b/trunk/security/runtime/org.argeo.security.mvc/.settings/org.maven.ide.eclipse.prefs
new file mode 100644 (file)
index 0000000..2169c43
--- /dev/null
@@ -0,0 +1,9 @@
+#Tue Sep 15 11:35:01 CEST 2009
+activeProfiles=
+eclipse.preferences.version=1
+fullBuildGoals=process-test-resources
+includeModules=false
+resolveWorkspaceProjects=true
+resourceFilterGoals=process-resources resources\:testResources
+skipCompilerPlugin=true
+version=1
diff --git a/trunk/security/runtime/org.argeo.security.mvc/build.properties b/trunk/security/runtime/org.argeo.security.mvc/build.properties
new file mode 100644 (file)
index 0000000..a740a34
--- /dev/null
@@ -0,0 +1,2 @@
+additional.bundles = org.springframework.beans
+source.. = src/main/java/
diff --git a/trunk/security/runtime/org.argeo.security.mvc/pom.xml b/trunk/security/runtime/org.argeo.security.mvc/pom.xml
new file mode 100644 (file)
index 0000000..312856d
--- /dev/null
@@ -0,0 +1,72 @@
+<?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.security</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.security.mvc</artifactId>
+       <name>Commons Security MVC</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>
+                                                       org.argeo.security.mvc.*
+                                               </Export-Package>
+                                               <Import-Package>*,javax.servlet</Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- Argeo Server -->
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.core</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Argeo Security -->
+               <dependency>
+                       <groupId>org.argeo.commons.security</groupId>
+                       <artifactId>org.argeo.security.core</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.web.servlet</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.aop</artifactId>
+               </dependency>
+
+               <!-- J2EE -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.servlet</artifactId>
+               </dependency>
+
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/security/runtime/org.argeo.security.mvc/src/main/java/org/argeo/security/mvc/ArgeoRememberMeServices.java b/trunk/security/runtime/org.argeo.security.mvc/src/main/java/org/argeo/security/mvc/ArgeoRememberMeServices.java
new file mode 100644 (file)
index 0000000..fde9f30
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007-2012 Mathieu Baudier
+ *
+ * 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.mvc;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.ui.rememberme.TokenBasedRememberMeServices;
+
+public class ArgeoRememberMeServices extends TokenBasedRememberMeServices {
+       public final static String DEFAULT_COOKIE_NAME = "ARGEO_SECURITY";
+
+       public ArgeoRememberMeServices() {
+               setCookieName(DEFAULT_COOKIE_NAME);
+       }
+
+       /**
+        * Sets a "cancel cookie" (with maxAge = 0) on the response to disable
+        * persistent logins.
+        * 
+        * @param request
+        * @param response
+        */
+       protected void cancelCookie(HttpServletRequest request,
+                       HttpServletResponse response) {
+               Cookie cookie = new Cookie(getCookieName(), null);
+               cookie.setMaxAge(0);
+               cookie.setPath("/");
+
+               response.addCookie(cookie);
+       }
+
+       /**
+        * Sets the cookie on the response
+        * 
+        * @param tokens
+        *            the tokens which will be encoded to make the cookie value.
+        * @param maxAge
+        *            the value passed to {@link Cookie#setMaxAge(int)}
+        * @param request
+        *            the request
+        * @param response
+        *            the response to add the cookie to.
+        */
+       protected void setCookie(String[] tokens, int maxAge,
+                       HttpServletRequest request, HttpServletResponse response) {
+               String cookieValue = encodeCookie(tokens);
+               Cookie cookie = new Cookie(getCookieName(), cookieValue);
+               cookie.setMaxAge(maxAge);
+               cookie.setPath("/");
+               response.addCookie(cookie);
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.mvc/src/main/java/org/argeo/security/mvc/ArgeoUserInterceptor.java b/trunk/security/runtime/org.argeo.security.mvc/src/main/java/org/argeo/security/mvc/ArgeoUserInterceptor.java
new file mode 100644 (file)
index 0000000..fd83e9f
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007-2012 Mathieu Baudier
+ *
+ * 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.mvc;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.argeo.security.UserAdminService;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+/** Add the current argeo user as an attribute to the request. */
+public class ArgeoUserInterceptor extends HandlerInterceptorAdapter {
+       private UserAdminService securityService;
+
+       @Override
+       public boolean preHandle(HttpServletRequest request,
+                       HttpServletResponse response, Object handler) throws Exception {
+               //request.setAttribute("argeoUser", securityService.getCurrentUser());
+               return super.preHandle(request, response, handler);
+       }
+
+       public void setSecurityService(UserAdminService securityService) {
+               this.securityService = securityService;
+       }
+
+}
diff --git a/trunk/security/runtime/org.argeo.security.mvc/src/main/java/org/argeo/security/mvc/UsersRolesController.java b/trunk/security/runtime/org.argeo.security.mvc/src/main/java/org/argeo/security/mvc/UsersRolesController.java
new file mode 100644 (file)
index 0000000..185d376
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2007-2012 Mathieu Baudier
+ *
+ * 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.mvc;
+
+import org.argeo.server.mvc.MvcConstants;
+import org.springframework.stereotype.Controller;
+
+@Controller
+public class UsersRolesController implements MvcConstants {
+//     private ArgeoSecurityService securityService;
+//     private Deserializer userDeserializer = null;
+
+       /* USER */
+
+//     @RequestMapping("/getCredentials.*")
+//     @ModelAttribute("user")
+//     public ArgeoUser getCredentials() {
+//             ArgeoUser argeoUser = securityService.getCurrentUser();
+//             if (argeoUser == null)
+//                     return new SimpleArgeoUser();
+//             else
+//                     return argeoUser;
+//     }
+//
+//     @RequestMapping("/getUsersList.*")
+//     @ModelAttribute("users")
+//     public Set<ArgeoUser> getUsersList() {
+//             return securityService.listUsers();
+//     }
+//
+//     @RequestMapping("/userExists.*")
+//     public BooleanAnswer userExists(@RequestParam("username") String username) {
+//             return new BooleanAnswer(securityService.userExists(username));
+//     }
+//
+//     @RequestMapping("/createUser.*")
+//     @ModelAttribute("user")
+//     public ArgeoUser createUser(Reader reader) {
+//             ArgeoUser user = userDeserializer.deserialize(reader,
+//                             SimpleArgeoUser.class);
+//             securityService.newUser(user);
+//             return securityService.getUser(user.getUsername());
+//     }
+//
+//     @RequestMapping("/updateUser.*")
+//     @ModelAttribute("user")
+//     public ArgeoUser updateUser(Reader reader) {
+//             ArgeoUser user = userDeserializer.deserialize(reader,
+//                             SimpleArgeoUser.class);
+//             securityService.updateUser(user);
+//             return securityService.getUser(user.getUsername());
+//     }
+//
+//     @RequestMapping("/updateUserSelf.*")
+//     @ModelAttribute("user")
+//     /** Will only update the user natures.*/
+//     public ArgeoUser updateUserSelf(Reader reader) {
+//             ArgeoUser user = securityService.getCurrentUser();
+//             ArgeoUser userForNatures = userDeserializer.deserialize(reader,
+//                             SimpleArgeoUser.class);
+//             user.updateUserNatures(userForNatures.getUserNatures());
+//             securityService.updateUser(user);
+//             return securityService.getUser(user.getUsername());
+//     }
+//
+//     @RequestMapping("/deleteUser.*")
+//     public ServerAnswer deleteUser(@RequestParam("username") String username) {
+//             securityService.deleteUser(username);
+//             return ServerAnswer.ok("User " + username + " deleted");
+//     }
+//
+//     @RequestMapping("/getUserDetails.*")
+//     @ModelAttribute("user")
+//     public ArgeoUser getUserDetails(@RequestParam("username") String username) {
+//             return securityService.getUser(username);
+//     }
+
+       /* ROLE */
+//     @RequestMapping("/getRolesList.*")
+//     @ModelAttribute("roles")
+//     public Set<String> getEditableRolesList() {
+//             return securityService.listEditableRoles();
+//     }
+//
+//     @RequestMapping("/createRole.*")
+//     public ServerAnswer createRole(@RequestParam("role") String role) {
+//             securityService.newRole(role);
+//             return ServerAnswer.ok("Role " + role + " created");
+//     }
+//
+//     @RequestMapping("/deleteRole.*")
+//     public ServerAnswer deleteRole(@RequestParam("role") String role) {
+//             securityService.deleteRole(role);
+//             return ServerAnswer.ok("Role " + role + " deleted");
+//     }
+//
+//     @RequestMapping("/updateUserPassword.*")
+//     public ServerAnswer updateUserPassword(
+//                     @RequestParam("username") String username,
+//                     @RequestParam("password") String password) {
+//             securityService.updateUserPassword(username, password);
+//             return ServerAnswer.ok("Password updated for user " + username);
+//     }
+//
+//     @RequestMapping("/updatePassword.*")
+//     public ServerAnswer updatePassword(
+//                     @RequestParam("oldPassword") String oldPassword,
+//                     @RequestParam("password") String password) {
+//             securityService.updateCurrentUserPassword(oldPassword, password);
+//             return ServerAnswer.ok("Password updated");
+//     }
+//
+//     public void setUserDeserializer(Deserializer userDeserializer) {
+//             this.userDeserializer = userDeserializer;
+//     }
+//
+//     public void setSecurityService(ArgeoSecurityService securityService) {
+//             this.securityService = securityService;
+//     }
+
+}
diff --git a/trunk/security/runtime/pom.xml b/trunk/security/runtime/pom.xml
new file mode 100644 (file)
index 0000000..c29a382
--- /dev/null
@@ -0,0 +1,21 @@
+<?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">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <version>2.1.11</version>
+               <artifactId>security</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <groupId>org.argeo.commons.security</groupId>
+       <artifactId>runtime</artifactId>
+       <name>Commons Security Runtime</name>
+       <packaging>pom</packaging>
+       <modules>
+               <module>org.argeo.security.core</module>
+               <module>org.argeo.security.ldap</module>
+               <module>org.argeo.security.activemq</module>
+               <module>org.argeo.security.jackrabbit</module>
+               <module>org.argeo.security.mvc</module>
+       </modules>
+</project>
\ No newline at end of file
diff --git a/trunk/server/dep/org.argeo.server.dep.activemq/pom.xml b/trunk/server/dep/org.argeo.server.dep.activemq/pom.xml
new file mode 100644 (file)
index 0000000..199d733
--- /dev/null
@@ -0,0 +1,93 @@
+<?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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>dep</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.dep.activemq</artifactId>
+       <packaging>pom</packaging>
+       <name>Commons Active MQ Dependencies</name>
+       <dependencies>
+               <!-- Commons Dep -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.log4j</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+               </dependency>
+
+               <!-- JMS / ActiveMQ -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.jms</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.activemq</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.pool</artifactId>
+               </dependency>
+
+               <!-- Required by ActiveMQ bundles -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.net</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.management.j2ee</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.ejb</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.xml.rpc</artifactId>
+               </dependency>
+
+               <!-- Test OSGi -->
+<!--           <dependency> -->
+<!--                   <groupId>org.argeo.commons.server</groupId> -->
+<!--                   <artifactId>org.argeo.server.dep.javax</artifactId> -->
+<!--                   <version>1.1.1-SNAPSHOT</version> -->
+<!--                   <type>pom</type> -->
+<!--                   <scope>test</scope> -->
+<!--           </dependency> -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.servlet</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.xmlcommons</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.xalan</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.beans</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.context</artifactId>
+                       <scope>test</scope>
+               </dependency>
+
+               <!-- For Active MQ optional -->
+<!--           <dependency> -->
+<!--                   <groupId>org.argeo.dep.osgi</groupId> -->
+<!--                   <artifactId>org.argeo.dep.osgi.activemq.optional</artifactId> -->
+<!--           </dependency> -->
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/dep/org.argeo.server.dep.ads/pom.xml b/trunk/server/dep/org.argeo.server.dep.ads/pom.xml
new file mode 100644 (file)
index 0000000..92eaa36
--- /dev/null
@@ -0,0 +1,54 @@
+<?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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>dep</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.dep.ads</artifactId>
+       <packaging>pom</packaging>
+       <name>Commons Apache Directory Server Dependencies</name>
+       <dependencies>
+               <!-- Commons Dep -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.log4j</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+               </dependency>
+
+               <!-- Apache Directory -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.directory.server.core</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.directory.shared.asn.codec</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.mina.filter.ssl</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.directory.server.jndi</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>jdbm</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.ext.jdbm</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <!-- TODO: version 2.4.0 is required by Spring-LDAP while Apache Directory 
+                       takes 2.1.0 -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.lang</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/dep/org.argeo.server.dep.hibernate/pom.xml b/trunk/server/dep/org.argeo.server.dep.hibernate/pom.xml
new file mode 100644 (file)
index 0000000..c8a7c26
--- /dev/null
@@ -0,0 +1,34 @@
+<?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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>dep</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.dep.hibernate</artifactId>
+       <packaging>pom</packaging>
+       <name>Commons Hibernate Dependencies</name>
+       <dependencies>
+               <!--  Commons Dep -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.log4j</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+               </dependency>
+
+               <!-- Hibernate -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.hibernate</artifactId>
+               </dependency>
+
+               <!-- JEE -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>edu.emory.mathcs.backport</artifactId>
+               </dependency>
+
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/dep/org.argeo.server.dep.jackrabbit.server/pom.xml b/trunk/server/dep/org.argeo.server.dep.jackrabbit.server/pom.xml
new file mode 100644 (file)
index 0000000..1494588
--- /dev/null
@@ -0,0 +1,79 @@
+<?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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>dep</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.dep.jackrabbit.server</artifactId>
+       <packaging>pom</packaging>
+       <name>Commons Jackrabbit Dependencies</name>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.jackrabbit</artifactId>
+                       <type>pom</type>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Enhancements of Jackrabbit -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>bcprov</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>bcmail</artifactId>
+               </dependency>
+               <!-- Provides org.bouncycastle.cms in v1.47 (bcmail not necessary anymore) -->
+               <!-- <dependency> -->
+               <!-- <groupId>org.bouncycastle</groupId> -->
+               <!-- <artifactId>bcpkix-jdk15on</artifactId> -->
+               <!-- </dependency> -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.xmlbeans</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.xmlcommons</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.pdfbox</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.poi</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>com.sun.syndication</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.cyberneko.html</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>de.l3s.boilerpipe</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.mail</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>com.drewnoakes.metadata_extractor</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>edu.ucar.unidata.netcdf</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.ccil.cowan.tagsoup</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/dep/org.argeo.server.dep.tomcat/pom.xml b/trunk/server/dep/org.argeo.server.dep.tomcat/pom.xml
new file mode 100644 (file)
index 0000000..c2b4538
--- /dev/null
@@ -0,0 +1,108 @@
+<?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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>dep</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.dep.tomcat</artifactId>
+       <packaging>pom</packaging>
+       <name>Commons Tomcat Dependencies</name>
+       <dependencies>
+               <!-- Commons Dep -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.log4j</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+               </dependency>
+
+               <!-- Modules -->
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.catalina</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.catalina.start</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- JEE -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.servlet</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.servlet.jsp</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.persistence</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.management.j2ee</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.ejb</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.xml.rpc</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.servlet.jsp.jstl</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.el</artifactId>
+               </dependency>
+
+               <!-- Taglibs -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.taglibs.standard</artifactId>
+               </dependency>
+
+               <!-- Tomcat -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.coyote</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.catalina</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.jasper</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.el</artifactId>
+               </dependency>
+
+               <!-- Test with Javax -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.xmlcommons</artifactId>
+                       <scope>test</scope>
+               </dependency>
+
+               <!-- OSGi test -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.osgi.boot</artifactId>
+                       <version>2.1.11</version>
+                       <scope>test</scope>
+               </dependency>
+
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/dep/pom.xml b/trunk/server/dep/pom.xml
new file mode 100644 (file)
index 0000000..623a47c
--- /dev/null
@@ -0,0 +1,55 @@
+<?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">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <version>2.1.11</version>
+               <artifactId>server</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <groupId>org.argeo.commons.server</groupId>
+       <artifactId>dep</artifactId>
+       <name>Commons Server Dependencies</name>
+       <packaging>pom</packaging>
+       <modules>
+               <module>org.argeo.server.dep.tomcat</module>
+               <module>org.argeo.server.dep.ads</module>
+               <module>org.argeo.server.dep.hibernate</module>
+               <module>org.argeo.server.dep.jackrabbit.server</module>
+               <module>org.argeo.server.dep.activemq</module>
+       </modules>
+       <profiles>
+               <profile>
+                       <id>check-osgi</id>
+                       <build>
+                               <plugins>
+                                       <plugin>
+                                               <groupId>org.argeo.maven.plugins</groupId>
+                                               <artifactId>maven-argeo-osgi-plugin</artifactId>
+                                               <executions>
+                                                       <execution>
+                                                               <id>check-osgi</id>
+                                                               <phase>test</phase>
+                                                               <goals>
+                                                                       <goal>equinox</goal>
+                                                               </goals>
+                                                               <configuration>
+                                                                       <onlyCheck>true</onlyCheck>
+                                                               </configuration>
+                                                       </execution>
+                                               </executions>
+                                       </plugin>
+                               </plugins>
+                       </build>
+                       <dependencies>
+                               <!-- OSGi test -->
+                               <dependency>
+                                       <groupId>org.argeo.commons.base</groupId>
+                                       <artifactId>org.argeo.osgi.boot</artifactId>
+                                       <version>2.1.11</version>
+                                       <scope>test</scope>
+                               </dependency>
+                       </dependencies>
+               </profile>
+       </profiles>
+</project>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.ext.bsf/pom.xml b/trunk/server/modules/org.argeo.ext.bsf/pom.xml
new file mode 100644 (file)
index 0000000..b84995b
--- /dev/null
@@ -0,0 +1,29 @@
+<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.server</groupId>
+               <artifactId>modules</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.ext.bsf</artifactId>
+       <name>Commons Server BSF Extension</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Fragment-Host>org.apache.bsf</Fragment-Host>
+                                               <Import-Package>
+                                                       *,
+                                                       bsh.util;resolution:=optional,
+                                                       org.codehaus.groovy.bsf;resolution:=optional
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/.project b/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/.project
new file mode 100644 (file)
index 0000000..6511be8
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.ext.jackrabbit.sybase</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/.settings/org.eclipse.pde.core.prefs b/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..32c9e3f
--- /dev/null
@@ -0,0 +1,4 @@
+#Tue Jun 15 19:05:36 CEST 2010
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/build.properties b/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/build.properties
new file mode 100644 (file)
index 0000000..5f22cdd
--- /dev/null
@@ -0,0 +1 @@
+bin.includes = META-INF/
diff --git a/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/org/apache/jackrabbit/core/fs/db/sybase.ddl b/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/org/apache/jackrabbit/core/fs/db/sybase.ddl
new file mode 100644 (file)
index 0000000..fb8a0e8
--- /dev/null
@@ -0,0 +1,16 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+create table ${schemaObjectPrefix}FSENTRY (FSENTRY_PATH varchar(2048) not null, FSENTRY_NAME varchar(255) not null, FSENTRY_DATA image null, FSENTRY_LASTMOD decimal(19,0) not null, FSENTRY_LENGTH decimal(19,0) not null)
+#create unique index ${schemaObjectPrefix}FSENTRY_IDX on ${schemaObjectPrefix}FSENTRY (FSENTRY_PATH, FSENTRY_NAME)
diff --git a/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/org/apache/jackrabbit/core/journal/sybase.ddl b/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/org/apache/jackrabbit/core/journal/sybase.ddl
new file mode 100644 (file)
index 0000000..d0f6242
--- /dev/null
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more\r
+#  contributor license agreements.  See the NOTICE file distributed with\r
+#  this work for additional information regarding copyright ownership.\r
+#  The ASF licenses this file to You under the Apache License, Version 2.0\r
+#  (the "License"); you may not use this file except in compliance with\r
+#  the License.  You may obtain a copy of the License at\r
+#\r
+#      http://www.apache.org/licenses/LICENSE-2.0\r
+#\r
+#  Unless required by applicable law or agreed to in writing, software\r
+#  distributed under the License is distributed on an "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+#  See the License for the specific language governing permissions and\r
+#  limitations under the License.\r
+\r
+create table ${schemaObjectPrefix}JOURNAL (REVISION_ID decimal(19,0) NOT NULL, JOURNAL_ID varchar(255), PRODUCER_ID varchar(255), REVISION_DATA IMAGE)\r
+create unique index ${schemaObjectPrefix}JOURNAL_IDX on ${schemaObjectPrefix}JOURNAL (REVISION_ID)\r
+create table ${schemaObjectPrefix}GLOBAL_REVISION (REVISION_ID decimal(19,0) NOT NULL)\r
+create unique index ${schemaObjectPrefix}GLOBAL_REVISION_IDX on ${schemaObjectPrefix}GLOBAL_REVISION (REVISION_ID)\r
+\r
+# Inserting the one and only revision counter record now helps avoiding race conditions\r
+insert into ${schemaObjectPrefix}GLOBAL_REVISION VALUES(0)\r
diff --git a/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/org/apache/jackrabbit/core/persistence/bundle/sybase.ddl b/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/org/apache/jackrabbit/core/persistence/bundle/sybase.ddl
new file mode 100644 (file)
index 0000000..47c6b71
--- /dev/null
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+create table ${schemaObjectPrefix}BUNDLE (NODE_ID binary(16) not null, BUNDLE_DATA image not null)
+create unique index ${schemaObjectPrefix}BUNDLE_IDX on ${schemaObjectPrefix}BUNDLE (NODE_ID)
+create table ${schemaObjectPrefix}REFS (NODE_ID binary(16) not null, REFS_DATA image not null)
+create unique index ${schemaObjectPrefix}REFS_IDX on ${schemaObjectPrefix}REFS (NODE_ID)
+create table ${schemaObjectPrefix}BINVAL (BINVAL_ID varchar(64) not null, BINVAL_DATA image not null)
+create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)
+#create table ${schemaObjectPrefix}NAMES (ID INTEGER IDENTITY(1,1) PRIMARY KEY, NAME varchar(255) COLLATE Latin1_General_CS_AS not null)
+create table ${schemaObjectPrefix}NAMES (ID INTEGER IDENTITY PRIMARY KEY, NAME varchar(255))
diff --git a/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/org/apache/jackrabbit/core/persistence/db/sybase.ddl b/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/org/apache/jackrabbit/core/persistence/db/sybase.ddl
new file mode 100644 (file)
index 0000000..2bde323
--- /dev/null
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA image not null)
+create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
+create table ${schemaObjectPrefix}PROP (PROP_ID varchar(1024) not null, PROP_DATA image not null)
+create unique index ${schemaObjectPrefix}PROP_IDX on ${schemaObjectPrefix}PROP (PROP_ID)
+create table ${schemaObjectPrefix}REFS (NODE_ID char(36) not null, REFS_DATA image not null)
+create unique index ${schemaObjectPrefix}REFS_IDX on ${schemaObjectPrefix}REFS (NODE_ID)
+create table ${schemaObjectPrefix}BINVAL (BINVAL_ID varchar(1024) not null, BINVAL_DATA image not null)
+create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)
diff --git a/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/pom.xml b/trunk/server/modules/org.argeo.ext.jackrabbit.sybase/pom.xml
new file mode 100644 (file)
index 0000000..3ecc41f
--- /dev/null
@@ -0,0 +1,25 @@
+<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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.ext.jackrabbit.sybase</artifactId>
+       <name>Commons Server Jackrabbit Sybase Extension</name>
+       <description>Provides experimental support to Sybase in Jackrabbit</description>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Fragment-Host>org.apache.jackrabbit</Fragment-Host>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.ext.jdbm/.project b/trunk/server/modules/org.argeo.ext.jdbm/.project
new file mode 100644 (file)
index 0000000..b1d7029
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.ext.jdbm</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/server/modules/org.argeo.ext.jdbm/build.properties b/trunk/server/modules/org.argeo.ext.jdbm/build.properties
new file mode 100644 (file)
index 0000000..5f22cdd
--- /dev/null
@@ -0,0 +1 @@
+bin.includes = META-INF/
diff --git a/trunk/server/modules/org.argeo.ext.jdbm/pom.xml b/trunk/server/modules/org.argeo.ext.jdbm/pom.xml
new file mode 100644 (file)
index 0000000..394122d
--- /dev/null
@@ -0,0 +1,32 @@
+<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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.ext.jdbm</artifactId>
+       <name>Commons Server JDBM Extension</name>
+       <description>Add Apache Directory Server support to JDBM</description>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Fragment-Host>jdbm</Fragment-Host>
+                                               <Import-Package>
+                                                       org.apache.directory.server.core.partition.impl.btree.jdbm;resolution:=optional,
+                                                       org.apache.directory.server.core.schema;resolution:=optional,
+                                                       org.apache.directory.server.core.partition.impl;resolution:=optional,
+                                                       org.apache.directory.server.core.partition.impl.btree;resolution:=optional,
+                                                       org.apache.directory.shared.ldap.message;resolution:=optional,
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.jackrabbit.webapp/.project b/trunk/server/modules/org.argeo.jackrabbit.webapp/.project
new file mode 100644 (file)
index 0000000..37eed05
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.jackrabbit.webapp</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/server/modules/org.argeo.jackrabbit.webapp/.settings/org.eclipse.pde.core.prefs b/trunk/server/modules/org.argeo.jackrabbit.webapp/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..ee0c8b2
--- /dev/null
@@ -0,0 +1,4 @@
+#Tue Feb 22 17:15:24 CET 2011
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/applicationContext.xml b/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/applicationContext.xml
new file mode 100644 (file)
index 0000000..723e6c7
--- /dev/null
@@ -0,0 +1,34 @@
+<?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:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
+
+       <import resource="osgi.xml" />
+       <import resource="security-filters.xml" />
+
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:jackrabbit-webapp.properties</value>
+               </property>
+       </bean>
+
+       <!-- The JCR sessions, optimised per web session. -->
+       <bean id="scopedSessionProvider" init-method="init"
+               destroy-method="destroy" class="org.argeo.jackrabbit.remote.ScopedSessionProvider"
+               scope="session">
+               <aop:scoped-proxy proxy-target-class="false" />
+       </bean>
+
+       <!-- The JCR sessions, one login per request. -->
+       <bean id="openInViewSessionProvider" init-method="init"
+               destroy-method="destroy" class="org.argeo.jackrabbit.remote.OpenInViewSessionProvider">
+       </bean>
+
+       <!-- <bean id="repositoryRegister" class="org.argeo.jcr.DefaultRepositoryRegister" 
+               /> -->
+
+</beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/osgi.xml b/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/osgi.xml
new file mode 100644 (file)
index 0000000..b4a3c48
--- /dev/null
@@ -0,0 +1,28 @@
+<?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:security="http://www.springframework.org/schema/security"\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/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">\r
+\r
+       <!-- <set id="repositories" interface="javax.jcr.Repository" -->\r
+       <!-- cardinality="0..N"> -->\r
+       <!-- <listener ref="repositoryRegister" bind-method="register" -->\r
+       <!-- unbind-method="unregister" /> -->\r
+       <!-- </set> -->\r
+       <!-- <list id="repositories" interface="javax.jcr.Repository" -->\r
+       <!-- cardinality="0..N"> -->\r
+       <!-- <listener ref="repositoryRegister" bind-method="register" -->\r
+       <!-- unbind-method="unregister" /> -->\r
+       <!-- </list> -->\r
+\r
+       <reference id="repositoryFactory" interface="javax.jcr.RepositoryFactory" />\r
+\r
+       <reference id="authenticationManager"\r
+               interface="org.springframework.security.AuthenticationManager" />\r
+       <reference id="userDetailsService"\r
+               interface="org.springframework.security.userdetails.UserDetailsService" />\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/remoting-servlet.xml b/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/remoting-servlet.xml
new file mode 100644 (file)
index 0000000..f5cd4d6
--- /dev/null
@@ -0,0 +1,22 @@
+<?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:aop="http://www.springframework.org/schema/aop"
+       xmlns:tx="http://www.springframework.org/schema/tx"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
+       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
+
+       <bean
+               class="org.springframework.web.servlet.handler.SimpleServletPostProcessor" />
+
+       <bean id="servletHandler"
+               class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter" />
+
+       <bean id="urlMapping"
+               class="org.argeo.jackrabbit.remote.JcrRemotingHandlerMapping">
+               <property name="repositoryFactory" ref="repositoryFactory" />
+               <property name="sessionProvider" ref="scopedSessionProvider" />
+       </bean>
+
+</beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security-filters.xml b/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security-filters.xml
new file mode 100644 (file)
index 0000000..52238a7
--- /dev/null
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:sec="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
+
+       <bean id="filterChain.davex" parent="filterChain.template">
+               <sec:filter-chain-map path-type="ant">
+                       <sec:filter-chain pattern="/*/*/*/**"
+                               filters="session,x509,basic,exception,interceptor" />
+                       <!-- For some reason the first level listing workspaces must be public -->
+                       <sec:filter-chain pattern="/*/*/"
+                               filters="anonymous,exception,interceptorPublic" />
+               </sec:filter-chain-map>
+       </bean>
+
+       <bean id="filterChain.private" parent="filterChain.template">
+               <sec:filter-chain-map path-type="ant">
+                       <sec:filter-chain pattern="/**"
+                               filters="session,x509,basic,exception,interceptor" />
+               </sec:filter-chain-map>
+       </bean>
+
+       <bean id="filterChain.public" parent="filterChain.template">
+               <sec:filter-chain-map path-type="ant">
+                       <sec:filter-chain pattern="/**"
+                               filters="anonymous,exception,interceptorPublic" />
+               </sec:filter-chain-map>
+       </bean>
+
+       <bean id="filterChain.template" abstract="true"
+               class="org.springframework.security.util.FilterChainProxy">
+               <property name="matcher">
+                       <bean class="org.springframework.security.util.AntUrlPathMatcher">
+                               <!-- Do not convert to lower case -->
+                               <constructor-arg value="false" />
+                       </bean>
+               </property>
+       </bean>
+
+       <!-- The actual authorization checks (called last, but first here for ease 
+               of configuration) -->
+       <bean id="interceptor" parent="filterInvocationInterceptorTemplate">
+               <property name="objectDefinitionSource">
+                       <value>
+                               PATTERN_TYPE_APACHE_ANT
+                               /**=ROLE_USER,ROLE_ADMIN
+                       </value>
+               </property>
+       </bean>
+       <bean id="interceptorPublic" parent="filterInvocationInterceptorTemplate">
+               <property name="objectDefinitionSource">
+                       <value>
+                               PATTERN_TYPE_APACHE_ANT
+                               /**=IS_AUTHENTICATED_ANONYMOUSLY
+                       </value>
+               </property>
+       </bean>
+
+       <bean id="x509"
+               class="org.springframework.security.ui.preauth.x509.X509PreAuthenticatedProcessingFilter">
+               <property name="authenticationManager" ref="authenticationManager" />
+               <property name="principalExtractor">
+                       <bean
+                               class="org.springframework.security.ui.preauth.x509.SubjectDnX509PrincipalExtractor">
+                               <property name="subjectDnRegex" value="CN=(.*?)," />
+                       </bean>
+               </property>
+       </bean>
+
+       <!-- Integrates the authentication information in the http sessions -->
+       <bean id="session"
+               class="org.springframework.security.context.HttpSessionContextIntegrationFilter">
+               <property name="allowSessionCreation" value="true" />
+       </bean>
+
+       <!-- Processes logouts, removing both session informations and the remember-me 
+               cookie from the browser -->
+       <!-- <bean id="logout" class="org.springframework.security.ui.logout.LogoutFilter"> -->
+       <!-- <constructor-arg value="/webdav/node/main" /> -->
+       <!-- <constructor-arg> -->
+       <!-- <list> -->
+       <!-- <bean -->
+       <!-- class="org.springframework.security.ui.logout.SecurityContextLogoutHandler" 
+               /> -->
+       <!-- </list> -->
+       <!-- </constructor-arg> -->
+       <!-- </bean> -->
+
+       <!-- Basic authentication -->
+       <bean id="basic"
+               class="org.springframework.security.ui.basicauth.BasicProcessingFilter">
+               <property name="authenticationManager">
+                       <ref bean="authenticationManager" />
+               </property>
+               <property name="authenticationEntryPoint">
+                       <ref local="basicProcessingFilterEntryPoint" />
+               </property>
+       </bean>
+
+       <!-- Activate basic auth when needed -->
+       <bean id="basicProcessingFilterEntryPoint"
+               class="org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint">
+               <property name="realmName">
+                       <value>${argeo.server.realmName}</value>
+               </property>
+       </bean>
+
+       <!-- If everything else failed, anonymous authentication -->
+       <bean id="anonymous"
+               class="org.springframework.security.providers.anonymous.AnonymousProcessingFilter">
+               <property name="key" value="${argeo.security.systemKey}" />
+               <property name="userAttribute" value="anonymous,ROLE_ANONYMOUS" />
+       </bean>
+
+       <!-- Reacts to security related exceptions -->
+       <bean id="exception"
+               class="org.springframework.security.ui.ExceptionTranslationFilter">
+               <property name="authenticationEntryPoint">
+                       <ref bean="basicProcessingFilterEntryPoint" />
+               </property>
+               <property name="accessDeniedHandler">
+                       <bean class="org.springframework.security.ui.AccessDeniedHandlerImpl">
+                               <!-- <property name="errorPage" value="/accessDenied.jsp" /> -->
+                       </bean>
+               </property>
+       </bean>
+
+       <!-- Template for authorization checks -->
+       <bean id="filterInvocationInterceptorTemplate" abstract="true"
+               class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
+               <property name="authenticationManager" ref="authenticationManager" />
+               <property name="accessDecisionManager">
+                       <bean class="org.springframework.security.vote.AffirmativeBased">
+                               <property name="allowIfAllAbstainDecisions" value="false" />
+                               <property name="decisionVoters">
+                                       <list>
+                                               <bean class="org.springframework.security.vote.RoleVoter" />
+                                               <bean class="org.springframework.security.vote.AuthenticatedVoter" />
+                                       </list>
+                               </property>
+                       </bean>
+               </property>
+       </bean>
+</beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/web.xml b/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..a1876b3
--- /dev/null
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+       version="2.5">
+
+       <display-name>Argeo Jackrabbit Webapp</display-name>
+
+       <!-- We don't want the session-scoped JCR sessions to wait too long before 
+               being logged out. -->
+       <session-config>
+               <session-timeout>5</session-timeout>
+       </session-config>
+
+       <!-- General -->
+       <context-param>
+               <param-name>contextConfigLocation</param-name>
+               <param-value>/WEB-INF/applicationContext.xml</param-value>
+       </context-param>
+
+       <listener>
+               <display-name>Spring Context</display-name>
+               <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+       </listener>
+       <context-param>
+               <param-name>contextClass</param-name>
+               <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
+       </context-param>
+
+       <!-- Remoting -->
+       <servlet>
+               <servlet-name>remoting</servlet-name>
+               <servlet-class>org.argeo.jackrabbit.remote.ExtendedDispatcherServlet</servlet-class>
+               <init-param>
+                       <param-name>contextClass</param-name>
+                       <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
+               </init-param>
+               <init-param>
+                       <param-name>dispatchOptionsRequest</param-name>
+                       <param-value>true</param-value>
+               </init-param>
+               <load-on-startup>1</load-on-startup>
+       </servlet>
+
+       <servlet-mapping>
+               <servlet-name>remoting</servlet-name>
+               <url-pattern>/jcr/*</url-pattern>
+       </servlet-mapping>
+
+       <servlet-mapping>
+               <servlet-name>remoting</servlet-name>
+               <url-pattern>/pub/*</url-pattern>
+       </servlet-mapping>
+
+       <!-- WEBDAV servlet -->
+       <servlet>
+               <servlet-name>webdav</servlet-name>
+               <servlet-class>org.argeo.jackrabbit.remote.ExtendedDispatcherServlet</servlet-class>
+               <init-param>
+                       <param-name>contextClass</param-name>
+                       <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
+               </init-param>
+               <init-param>
+                       <param-name>dispatchOptionsRequest</param-name>
+                       <param-value>true</param-value>
+               </init-param>
+               <load-on-startup>1</load-on-startup>
+       </servlet>
+
+       <servlet-mapping>
+               <servlet-name>webdav</servlet-name>
+               <url-pattern>/files/*</url-pattern>
+       </servlet-mapping>
+
+       <servlet-mapping>
+               <servlet-name>webdav</servlet-name>
+               <url-pattern>/public/*</url-pattern>
+       </servlet-mapping>
+
+       <!-- Security -->
+       <filter>
+               <filter-name>filterChain.davex</filter-name>
+               <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
+       </filter>
+       <filter-mapping>
+               <filter-name>filterChain.davex</filter-name>
+               <url-pattern>/jcr/*</url-pattern>
+       </filter-mapping>
+
+       <filter>
+               <filter-name>filterChain.private</filter-name>
+               <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
+       </filter>
+       <filter-mapping>
+               <filter-name>filterChain.private</filter-name>
+               <url-pattern>/files/*</url-pattern>
+       </filter-mapping>
+
+       <filter>
+               <filter-name>filterChain.public</filter-name>
+               <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
+       </filter>
+       <filter-mapping>
+               <filter-name>filterChain.public</filter-name>
+               <url-pattern>/pub/*</url-pattern>
+       </filter-mapping>
+       <filter-mapping>
+               <filter-name>filterChain.public</filter-name>
+               <url-pattern>/public/*</url-pattern>
+       </filter-mapping>
+
+</web-app>
diff --git a/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/webdav-config.xml b/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/webdav-config.xml
new file mode 100644 (file)
index 0000000..da4e18b
--- /dev/null
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+  -->
+<!--
+<!DOCTYPE config [
+        <!ELEMENT config (iomanager , propertymanager, (collection | noncollection)? , filter?, mimetypeproperties?) >
+
+        <!ELEMENT iomanager (class, iohandler*) >
+        <!ELEMENT iohandler (class) >
+
+        <!ELEMENT propertymanager (class, propertyhandler*) >
+        <!ELEMENT propertyhandler (class) >
+
+        <!ELEMENT collection (nodetypes) >
+        <!ELEMENT noncollection (nodetypes) >
+
+        <!ELEMENT filter (class, namespaces?, nodetypes?) >
+
+        <!ELEMENT class >
+        <!ATTLIST class
+            name  CDATA #REQUIRED
+        >
+        <!ELEMENT namespaces (prefix | uri)* >
+        <!ELEMENT prefix (CDATA) >
+        <!ELEMENT uri (CDATA) >
+
+        <!ELEMENT nodetypes (nodetype)* >
+        <!ELEMENT nodetype (CDATA) >
+
+        <!ELEMENT mimetypeproperties (mimemapping*, defaultmimetype) >
+
+        <!ELEMENT mimemapping >
+        <!ATTLIST mimemapping
+            extension  CDATA #REQUIRED
+            mimetype  CDATA #REQUIRED
+        >
+
+        <!ELEMENT defaultmimetype (CDATA) >
+]>
+-->
+
+<config>
+    <!--
+     Defines the IOManager implementation that is responsible for passing
+     import/export request to the individual IO-handlers.
+    -->
+    <iomanager>
+        <!-- class element defines the manager to be used. The specified class
+             must implement the IOManager interface.
+             Note, that the handlers are being added and called in the order
+             they appear in the configuration.
+        -->
+        <class name="org.apache.jackrabbit.server.io.IOManagerImpl" />
+        <iohandler>
+            <class name="org.apache.jackrabbit.server.io.VersionHandler" />
+        </iohandler>
+        <iohandler>
+            <class name="org.apache.jackrabbit.server.io.VersionHistoryHandler" />
+        </iohandler>
+<!--         <iohandler> -->
+<!--             <class name="org.apache.jackrabbit.server.io.ZipHandler" /> -->
+<!--         </iohandler> -->
+<!--         <iohandler> -->
+<!--             <class name="org.apache.jackrabbit.server.io.XmlHandler" /> -->
+<!--         </iohandler> -->
+        <iohandler>
+            <class name="org.apache.jackrabbit.server.io.DirListingExportHandler" />
+        </iohandler>
+        <iohandler>
+            <class name="org.apache.jackrabbit.server.io.DefaultHandler" />
+        </iohandler>
+    </iomanager>
+    <!--
+     Example config for iomanager that populates its list of handlers with
+     default values. Therefore the 'iohandler' elements are omited.
+    -->
+    <!--
+    <iomanager>
+        <class name="org.apache.jackrabbit.server.io.DefaultIOManager" />
+    </iomanager>
+    -->
+    <!--
+     Defines the PropertyManager implementation that is responsible for export
+     and import of resource properties.
+    -->
+    <propertymanager>
+        <!-- class element defines the manager to be used. The specified class
+             must implement the PropertyManager interface.
+             Note, that the handlers are being added and called in the order
+             they appear in the configuration.
+        -->
+        <class name="org.apache.jackrabbit.server.io.PropertyManagerImpl" />
+        <propertyhandler>
+            <class name="org.apache.jackrabbit.server.io.VersionHandler" />
+        </propertyhandler>
+        <propertyhandler>
+            <class name="org.apache.jackrabbit.server.io.VersionHistoryHandler" />
+        </propertyhandler>
+<!--         <propertyhandler> -->
+<!--             <class name="org.apache.jackrabbit.server.io.ZipHandler" /> -->
+<!--         </propertyhandler> -->
+<!--         <propertyhandler> -->
+<!--             <class name="org.apache.jackrabbit.server.io.XmlHandler" /> -->
+<!--         </propertyhandler> -->
+        <propertyhandler>
+            <class name="org.apache.jackrabbit.server.io.DefaultHandler" />
+        </propertyhandler>
+    </propertymanager>
+    <!--
+     Define nodetypes, that should never by displayed as 'collection'
+    -->
+    <noncollection>
+        <nodetypes>
+            <nodetype>nt:file</nodetype>
+            <nodetype>nt:resource</nodetype>
+        </nodetypes>
+    </noncollection>
+    <!--
+     Example: Defines nodetypes, that should always be displayed as 'collection'.
+    -->
+    <!--
+    <collection>
+        <nodetypes>
+            <nodetype>nt:folder</nodetype>
+            <nodetype>rep:root</nodetype>
+        </nodetypes>
+    </collection>
+    -->
+    <!--
+     Filter that allows to prevent certain items from being displayed.
+     Please note, that this has an effect on PROPFIND calls only and does not
+     provide limited access to those items matching any of the filters.
+
+     However specifying a filter may cause problems with PUT or MKCOL if the
+     resource to be created is being filtered out, thus resulting in inconsistent
+     responses (e.g. PUT followed by PROPFIND on parent).
+     -->
+    <filter>
+        <!-- class element defines the resource filter to be used. The specified class
+             must implement the ItemFilter interface -->
+        <class name="org.apache.jackrabbit.webdav.simple.DefaultItemFilter" />
+        <!--
+         Nodetype names to be used to filter child nodes.
+         A child node can be filtered if the declaring nodetype of its definition
+         is one of the nodetype names specified in the nodetypes Element.
+         E.g. defining 'rep:root' as filtered nodetype whould result in jcr:system
+         being hidden but no other child node of the root node, since those
+         are defined by the nodetype nt:unstructered.
+        -->
+        <!--
+        <nodetypes>
+            <nodetype>rep:root</nodetype>
+        </nodetypes>
+        -->
+        <!--
+         Namespace prefixes or uris. Items having a name that matches any of the
+         entries will be filtered.
+        -->
+        <namespaces>
+            <prefix>rep</prefix>
+            <prefix>jcr</prefix>
+            <!--
+            <uri>internal</uri>
+            <uri>http://www.jcp.org/jcr/1.0</uri>
+            -->
+        </namespaces>
+    </filter>
+    
+    <!--
+     Optional 'mimetypeproperties' element.
+     It defines additional or replaces existing mappings for the MimeResolver
+     instance created by the ResourceConfig.
+     The default mappings are defined in org.apache.jackrabbit.server.io.mimetypes.properties.
+     If the default mime type defined by MimeResolver is 'application/octet-stream'.
+    -->
+    <!--
+    <mimetypeproperties>
+        <mimemapping extension="rtf" mimetype="application/rtf" />
+        <mimemapping extension="ott" mimetype="application/vnd.oasis.opendocument.text-template" />
+        <defaultmimetype>text/html</defaultmimetype>
+    </mimetypeproperties>
+    -->
+</config>
diff --git a/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/webdav-servlet.xml b/trunk/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/webdav-servlet.xml
new file mode 100644 (file)
index 0000000..561d20c
--- /dev/null
@@ -0,0 +1,21 @@
+<?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:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
+       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
+
+       <bean
+               class="org.springframework.web.servlet.handler.SimpleServletPostProcessor" />
+
+       <bean id="servletHandler"
+               class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter" />
+
+       <bean id="urlMapping" class="org.argeo.jackrabbit.remote.SimpleWebdavHandlerMapping">
+               <property name="configuration" value="/WEB-INF/webdav-config.xml" />
+               <property name="repositoryFactory" ref="repositoryFactory" />
+               <property name="sessionProvider" ref="openInViewSessionProvider" />
+       </bean>
+
+</beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.jackrabbit.webapp/build.properties b/trunk/server/modules/org.argeo.jackrabbit.webapp/build.properties
new file mode 100644 (file)
index 0000000..5f22cdd
--- /dev/null
@@ -0,0 +1 @@
+bin.includes = META-INF/
diff --git a/trunk/server/modules/org.argeo.jackrabbit.webapp/jackrabbit-webapp.properties b/trunk/server/modules/org.argeo.jackrabbit.webapp/jackrabbit-webapp.properties
new file mode 100644 (file)
index 0000000..47f47b9
--- /dev/null
@@ -0,0 +1,3 @@
+argeo.security.systemKey=argeo
+argeo.server.realmName=Argeo
+argeo.jcr.webapp.rememberMeValidity=3600
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.jackrabbit.webapp/pom.xml b/trunk/server/modules/org.argeo.jackrabbit.webapp/pom.xml
new file mode 100644 (file)
index 0000000..9707902
--- /dev/null
@@ -0,0 +1,61 @@
+<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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.jackrabbit.webapp</artifactId>
+       <name>Commons Server Jackrabbit Webapp</name>
+       <description>JCR remoting, WebDav</description>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Web-ContextPath>data</Web-ContextPath>
+                                               <!-- Import-Package needs to be specified explicitly since BND doesn't 
+                                                       scane WEB-INF -->
+                                               <Import-Package>
+                                                       *,
+                                                       javax.jcr,
+                                                       javax.servlet,
+                                                       javax.servlet.http,
+                                                       javax.servlet.resources,
+                                                       org.aopalliance.aop,
+                                                       org.argeo.jackrabbit.remote,
+                                                       org.argeo.jcr,
+                                                       org.argeo.jcr.mvc,
+                                                       org.springframework.aop,
+                                                       org.springframework.aop.framework,
+                                                       org.springframework.aop.scope,
+                                                       org.springframework.beans.factory.config,
+                                                       org.springframework.osgi.web.context.support,
+                                                       org.springframework.security,
+                                                       org.springframework.security.providers.anonymous,
+                                                       org.springframework.security.ui,
+                                                       org.springframework.security.ui.webapp,
+                                                       org.springframework.security.ui.preauth.x509,
+                                                       org.springframework.security.userdetails,
+                                                       org.springframework.web.context,
+                                                       org.springframework.web.filter,
+                                                       org.springframework.web.servlet,
+                                                       org.springframework.web.servlet.handler,
+                                                       org.springframework.web.servlet.view,
+                                                       org.springframework.security.util,
+                                                       org.springframework.security.context,
+                                                       org.springframework.security.ui.basicauth,
+                                                       org.springframework.security.intercept.web,
+                                                       org.springframework.security.vote,
+                                                       org.springframework.security.ui.logout,
+                                                       org.apache.jackrabbit.server,
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.node.repo.jackrabbit/.project b/trunk/server/modules/org.argeo.node.repo.jackrabbit/.project
new file mode 100644 (file)
index 0000000..648ec17
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.node.repo.jackrabbit</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/server/modules/org.argeo.node.repo.jackrabbit/.settings/org.eclipse.pde.core.prefs b/trunk/server/modules/org.argeo.node.repo.jackrabbit/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..aae2471
--- /dev/null
@@ -0,0 +1,4 @@
+#Wed Feb 23 10:53:42 CET 2011
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/trunk/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo-osgi.xml b/trunk/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo-osgi.xml
new file mode 100644 (file)
index 0000000..957dda1
--- /dev/null
@@ -0,0 +1,31 @@
+<?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
+       <!-- REFERENCE -->\r
+       <list id="repositories" interface="javax.jcr.Repository"\r
+               cardinality="0..N">\r
+               <listener ref="repositoryFactory" bind-method="register"\r
+                       unbind-method="unregister" />\r
+       </list>\r
+\r
+       <!-- SERVICES -->\r
+       <service ref="nodeJcrRepository">\r
+               <interfaces>\r
+                       <beans:value>javax.jcr.Repository</beans:value>\r
+                       <beans:value>org.argeo.jcr.MaintainedRepository</beans:value>\r
+               </interfaces>\r
+               <service-properties>\r
+                       <beans:entry key="argeo.jcr.repository.alias" value="node" />\r
+                       <beans:entry key="argeo.jcr.repository.home" value="${argeo.node.repo.home}" />\r
+               </service-properties>\r
+       </service>\r
+       <service ref="repositoryFactory" interface="javax.jcr.RepositoryFactory" />\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo.xml b/trunk/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo.xml
new file mode 100644 (file)
index 0000000..6648c15
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:noderepo.properties</value>
+               </property>
+       </bean>
+
+       <bean id="nodeJcrRepository" class="org.argeo.jackrabbit.JackrabbitContainer"
+               init-method="init" destroy-method="destroy">
+               <property name="homeDirectory" value="${argeo.node.repo.home}" />
+               <property name="configuration" value="${argeo.node.repo.configuration}" />
+               <property name="variables" value="osgibundle:/noderepo.properties" />
+               <property name="forceCndImport" value="${argeo.node.repo.forceCndImport}" />
+       </bean>
+
+       <bean id="repositoryFactory" class="org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory">
+               <property name="bundleContext" ref="bundleContext" />
+       </bean>
+
+</beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.node.repo.jackrabbit/build.properties b/trunk/server/modules/org.argeo.node.repo.jackrabbit/build.properties
new file mode 100644 (file)
index 0000000..a275ae6
--- /dev/null
@@ -0,0 +1,4 @@
+bin.includes = META-INF/,\
+               noderepo.properties,\
+               repository-h2.xml,\
+               repository-postgresql.xml
diff --git a/trunk/server/modules/org.argeo.node.repo.jackrabbit/noderepo.properties b/trunk/server/modules/org.argeo.node.repo.jackrabbit/noderepo.properties
new file mode 100644 (file)
index 0000000..c91d51d
--- /dev/null
@@ -0,0 +1,16 @@
+# Workspace used by the node session
+argeo.node.repo.defaultWorkspace=main
+#argeo.node.repo.securityWorkspace=security
+argeo.node.repo.forceCndImport=true
+
+# Repository base directory
+argeo.node.repo.home=${osgi.instance.area}/node
+
+## H2 Embedded (DEFAULT)
+argeo.node.repo.configuration=osgibundle:repository-h2.xml
+argeo.node.repo.dburl=jdbc:h2:${osgi.instance.area}/node/h2/repository
+argeo.node.repo.dbuser=sa
+argeo.node.repo.dbpassword=
+
+# ADVANCED
+argeo.node.repo.maxPoolSize=10
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.node.repo.jackrabbit/pom.xml b/trunk/server/modules/org.argeo.node.repo.jackrabbit/pom.xml
new file mode 100644 (file)
index 0000000..ae2086f
--- /dev/null
@@ -0,0 +1,40 @@
+<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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.node.repo.jackrabbit</artifactId>
+       <name>Commons Server Node Repository Jackrabbit</name>
+       <description>Default Node Repository Based on Jackrabbit</description>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-SymbolicName>${project.artifactId};singleton:=true</Bundle-SymbolicName>
+                                               <!-- Import-Package needs to be specified explicitly since BND doesn't 
+                                                       deal with custom Spring-Context instructions -->
+                                               <Spring-Context>META-INF/spring/*.xml;create-asynchronously:=false</Spring-Context>
+                                               <Import-Package>
+                                                       *,
+                                                       com.mysql.jdbc;resolution:=optional,
+                                                       org.h2;resolution:=optional,
+                                                       org.postgresql;resolution:=optional,
+                                                       javax.jcr,
+                                                       org.apache.jackrabbit.core,
+                                                       org.apache.jackrabbit.core.config,
+                                                       org.argeo.jackrabbit,
+                                                       org.argeo.jcr,
+                                                       org.springframework.beans.factory.config,
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.node.repo.jackrabbit/repository-h2.xml b/trunk/server/modules/org.argeo.node.repo.jackrabbit/repository-h2.xml
new file mode 100644 (file)
index 0000000..583bf4c
--- /dev/null
@@ -0,0 +1,79 @@
+<?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="${argeo.node.repo.dburl}" />
+                       <param name="user" value="${argeo.node.repo.dbuser}" />
+                       <param name="password" value="${argeo.node.repo.dbpassword}" />
+                       <param name="databaseType" value="h2" />
+                       <param name="maxPoolSize" value="${argeo.node.repo.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="${argeo.node.repo.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_" />
+               </PersistenceManager>
+               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+                       <param name="path" value="${wsp.home}/index" />
+                       <param name="initializeHierarchyCache" value="true" />
+               </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}/index" />
+               <param name="initializeHierarchyCache" value="true" />
+               <!-- <param name="extractorPoolSize" value="2" /> -->
+               <!-- <param name="supportHighlighting" value="true" /> -->
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security">
+               </SecurityManager>
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
+               </AccessManager>
+               <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
+               </LoginModule>
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.node.repo.jackrabbit/repository-memory.xml b/trunk/server/modules/org.argeo.node.repo.jackrabbit/repository-memory.xml
new file mode 100644 (file)
index 0000000..b41cfad
--- /dev/null
@@ -0,0 +1,52 @@
+<?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>
+       <!-- File system and datastore -->
+       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+
+       <!-- Workspace templates -->
+       <Workspaces rootPath="${rep.home}/workspaces"
+               defaultWorkspace="${argeo.node.repo.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" />
+               </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" />
+                       <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}/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">
+               </SecurityManager>
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
+               </AccessManager>
+               <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
+               </LoginModule>
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.node.repo.jackrabbit/repository-mysql.xml b/trunk/server/modules/org.argeo.node.repo.jackrabbit/repository-mysql.xml
new file mode 100644 (file)
index 0000000..208cd1d
--- /dev/null
@@ -0,0 +1,77 @@
+<?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="com.mysql.jdbc.Driver" />
+                       <param name="url" value="${argeo.node.repo.dburl}" />
+                       <param name="user" value="${argeo.node.repo.dbuser}" />
+                       <param name="password" value="${argeo.node.repo.dbpassword}" />
+                       <param name="databaseType" value="mysql" />
+                       <param name="maxPoolSize" value="${argeo.node.repo.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="mysql" />
+               <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="default" />
+       <Workspace name="${wsp.name}">
+               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schema" value="mysql" />
+                       <param name="schemaObjectPrefix" value="fs_${wsp.name}_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.MySqlPersistenceManager">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schemaObjectPrefix" value="pm_${wsp.name}_" />
+               </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="mysql" />
+                       <param name="schemaObjectPrefix" value="fs_ver_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.MySqlPersistenceManager">
+                       <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}/index" />
+               <param name="extractorPoolSize" value="2" />
+               <param name="supportHighlighting" value="true" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security">
+               </SecurityManager>
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
+               </AccessManager>
+               <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
+               </LoginModule>
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.node.repo.jackrabbit/repository-postgresql.xml b/trunk/server/modules/org.argeo.node.repo.jackrabbit/repository-postgresql.xml
new file mode 100644 (file)
index 0000000..811f0c6
--- /dev/null
@@ -0,0 +1,77 @@
+<?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.postgresql.Driver" />
+                       <param name="url" value="${argeo.node.repo.dburl}" />
+                       <param name="user" value="${argeo.node.repo.dbuser}" />
+                       <param name="password" value="${argeo.node.repo.dbpassword}" />
+                       <param name="databaseType" value="postgresql" />
+                       <param name="maxPoolSize" value="${argeo.node.repo.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="${argeo.node.repo.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_" />
+               </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="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_" />
+               </PersistenceManager>
+       </Versioning>
+
+       <!-- Indexing -->
+       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+               <param name="path" value="${rep.home}/index" />
+               <param name="extractorPoolSize" value="2" />
+               <param name="supportHighlighting" value="true" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security">
+               </SecurityManager>
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
+               </AccessManager>
+               <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
+               </LoginModule>
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.node.repofactory.jackrabbit/.project b/trunk/server/modules/org.argeo.node.repofactory.jackrabbit/.project
new file mode 100644 (file)
index 0000000..cff24eb
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.node.repofactory.jackrabbit</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/server/modules/org.argeo.node.repofactory.jackrabbit/META-INF/spring/repofactory-osgi.xml b/trunk/server/modules/org.argeo.node.repofactory.jackrabbit/META-INF/spring/repofactory-osgi.xml
new file mode 100644 (file)
index 0000000..b7c82c1
--- /dev/null
@@ -0,0 +1,21 @@
+<?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
+       <!-- REFERENCE -->\r
+<!--   <list id="repositories" interface="javax.jcr.Repository" -->\r
+<!--           cardinality="0..N"> -->\r
+<!--           <listener ref="repositoryFactory" bind-method="register" -->\r
+<!--                   unbind-method="unregister" /> -->\r
+<!--   </list> -->\r
+\r
+       <!-- SERVICES -->\r
+<!--   <service ref="repositoryFactory" interface="javax.jcr.RepositoryFactory" /> -->\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.node.repofactory.jackrabbit/META-INF/spring/repofactory.xml b/trunk/server/modules/org.argeo.node.repofactory.jackrabbit/META-INF/spring/repofactory.xml
new file mode 100644 (file)
index 0000000..a00c9b0
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+       <bean id="repositoryFactory" class="org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory">
+               <property name="bundleContext" ref="bundleContext" />
+       </bean>
+
+</beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.node.repofactory.jackrabbit/pom.xml b/trunk/server/modules/org.argeo.node.repofactory.jackrabbit/pom.xml
new file mode 100644 (file)
index 0000000..42e02ed
--- /dev/null
@@ -0,0 +1,34 @@
+<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.server</groupId>
+               <version>1.1.5-SNAPSHOT</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.node.repofactory.jackrabbit</artifactId>
+       <name>Commons Server Node Repository Factory Jackrabbit</name>
+       <description>Default JCR repository factory based on Jackrabbit. It provides access to aliased and remote repositories.</description>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-SymbolicName>${project.artifactId};singleton:=true</Bundle-SymbolicName>
+                                               <!-- Import-Package needs to be specified explicitly since BND doesn't 
+                                                       deal with custom Spring-Context instructions -->
+                                               <Spring-Context>META-INF/spring/*.xml;create-asynchronously:=false</Spring-Context>
+                                               <Import-Package>
+                                                       *,
+                                                       javax.jcr,
+                                                       org.argeo.jackrabbit,
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.activemq.broker/.project b/trunk/server/modules/org.argeo.server.activemq.broker/.project
new file mode 100644 (file)
index 0000000..0dd1ba1
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.activemq.broker</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/server/modules/org.argeo.server.activemq.broker/.settings/org.eclipse.pde.core.prefs b/trunk/server/modules/org.argeo.server.activemq.broker/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..c84159a
--- /dev/null
@@ -0,0 +1,4 @@
+#Wed Apr 13 20:39:49 CEST 2011
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/trunk/server/modules/org.argeo.server.activemq.broker/META-INF/spring/activemq-broker.xml b/trunk/server/modules/org.argeo.server.activemq.broker/META-INF/spring/activemq-broker.xml
new file mode 100644 (file)
index 0000000..eae474b
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:amq="http://activemq.apache.org/schema/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">
+
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="ignoreUnresolvablePlaceholders" value="true" />
+               <property name="locations">
+                       <value>osgibundle:activemq.properties
+                       </value>
+               </property>
+       </bean>
+
+       <!-- Embedded broker -->
+       <amq:broker id="broker" useJmx="false" persistent="false">
+               <amq:transportConnectors>
+                       <amq:transportConnector uri="${argeo.server.jms.url}" />
+                       <!-- <amq:transportConnector uri="xmpp://localhost:61222" /> -->
+               </amq:transportConnectors>
+       </amq:broker>
+</beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.activemq.broker/activemq.properties b/trunk/server/modules/org.argeo.server.activemq.broker/activemq.properties
new file mode 100644 (file)
index 0000000..56fc24c
--- /dev/null
@@ -0,0 +1 @@
+argeo.server.jms.url=tcp://localhost:61616
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.activemq.broker/build.properties b/trunk/server/modules/org.argeo.server.activemq.broker/build.properties
new file mode 100644 (file)
index 0000000..5f22cdd
--- /dev/null
@@ -0,0 +1 @@
+bin.includes = META-INF/
diff --git a/trunk/server/modules/org.argeo.server.activemq.broker/pom.xml b/trunk/server/modules/org.argeo.server.activemq.broker/pom.xml
new file mode 100644 (file)
index 0000000..e8df864
--- /dev/null
@@ -0,0 +1,45 @@
+<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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.activemq.broker</artifactId>
+       <name>Commons Server Default Active MQ Broker</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Import-Package>
+                                                       *,
+                                                       ietf.params.xml.ns.xmpp_sasl;resolution:=optional,
+                                                       ietf.params.xml.ns.xmpp_stanzas;resolution:=optional,
+                                                       ietf.params.xml.ns.xmpp_streams;resolution:=optional,
+                                                       ietf.params.xml.ns.xmpp_tls;resolution:=optional,
+                                                       jabber.client;resolution:=optional,
+                                                       jabber.iq._private;resolution:=optional,
+                                                       jabber.iq.auth;resolution:=optional,
+                                                       jabber.iq.roster;resolution:=optional,
+                                                       org.apache.activemq.broker,
+                                                       org.apache.activemq.command;resolution:=optional,
+                                                       org.apache.activemq.store.amq,
+                                                       org.apache.activemq.transport.http;resolution:=optional,
+                                                       org.apache.activemq.util,
+                                                       org.apache.activemq.xbean,
+                                                       org.jabber.etherx.streams;resolution:=optional,
+                                                       org.jabber.protocol.disco_info;resolution:=optional,
+                                                       org.jabber.protocol.disco_items;resolution:=optional,
+                                                       org.jabber.protocol.muc;resolution:=optional,
+                                                       org.jabber.protocol.muc_user;resolution:=optional,
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.ads.server/.project b/trunk/server/modules/org.argeo.server.ads.server/.project
new file mode 100644 (file)
index 0000000..a191480
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.ads.server</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/server/modules/org.argeo.server.ads.server/.settings/org.eclipse.pde.core.prefs b/trunk/server/modules/org.argeo.server.ads.server/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..5733b31
--- /dev/null
@@ -0,0 +1,4 @@
+#Sun Jan 16 09:58:38 CET 2011
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/trunk/server/modules/org.argeo.server.ads.server/META-INF/spring/ads.xml b/trunk/server/modules/org.argeo.server.ads.server/META-INF/spring/ads.xml
new file mode 100644 (file)
index 0000000..6fdf476
--- /dev/null
@@ -0,0 +1,48 @@
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:ads.properties</value>
+               </property>
+       </bean>
+
+       <bean class="org.argeo.server.ads.AdsContainer">
+               <property name="workingDirectory" value="${osgi.instance.area}/apacheds" />
+<!--           <property name="deleteWorkingDirOnExit" value="true" /> -->
+               <property name="configuration" ref="configuration" />
+               <property name="environment" ref="environment" />
+               <property name="ldifs">
+                       <list>
+                               <value>${argeo.ads.init.ldif}</value>
+                       </list>
+               </property>
+       </bean>
+
+       <bean id="environment"
+               class="org.springframework.beans.factory.config.PropertiesFactoryBean">
+               <property name="properties">
+                       <props>
+                               <prop key="java.naming.security.authentication">simple</prop>
+                               <prop key="java.naming.security.principal">${argeo.ldap.manager.userdn}</prop>
+                               <prop key="java.naming.security.credentials">${argeo.ldap.manager.password}</prop>
+                               <!--<prop key="kdc.entryBaseDn">ou=users,dc=example,dc=com</prop> -->
+                               <!--<prop key="kdc.java.naming.security.credentials">secret</prop> -->
+                               <!-- <prop key="changepw.entryBaseDn">ou=users,dc=example,dc=com</prop> -->
+                               <!-- <prop key="changepw.java.naming.security.credentials">secret</prop> -->
+                               <!-- Set this key to a space delimited set of attributeType descriptions 
+                                       and their OID's if you want an attributeType to be handled as binary content. 
+                                       The server will use the schema to derive the set of attributeTypes to treat 
+                                       as binary. The union if the values you provide here will be taken as the 
+                                       set of binaries. Note to be consistent you must add both the OID and all 
+                                       the names an attributeType can have. -->
+                               <!-- <prop key="java.naming.ldap.attributes.binary"></prop> -->
+                       </props>
+               </property>
+       </bean>
+
+
+</beans>
diff --git a/trunk/server/modules/org.argeo.server.ads.server/META-INF/spring/server.xml b/trunk/server/modules/org.argeo.server.ads.server/META-INF/spring/server.xml
new file mode 100644 (file)
index 0000000..84407be
--- /dev/null
@@ -0,0 +1,363 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
+  "http://www.springframework.org/dtd/spring-beans.dtd">
+
+<beans>
+       <bean id="configuration"
+               class="org.apache.directory.server.configuration.MutableServerStartupConfiguration">
+               <property name="workingDirectory" value="example.com" />
+
+               <!--
+                       Uncomment below to have the server load entries on startup!
+               -->
+               <!--
+                       ldifDirectory property can point to a relative file, directory or
+               -->
+               <!--
+                       can point to an absolute path to either using the URL path
+               -->
+               <!--
+                       notation: i.e. file:///Users/jack/apacheds/ldifs
+               -->
+
+               <!--
+                       Entries will optionally be filtered using LdifLoadFilters in the
+               -->
+               <!--
+                       order specified. The included Krb5KdcEntryFilter will filter
+               -->
+               <!--
+                       kerberos principals creating keys for them using their
+               -->
+               <!--
+                       userPassword attribute if present.
+               -->
+
+               <!--
+               <property name="ldifDirectory">
+                       <value>${argeo.ads.init.ldif}</value>
+               </property> -->
+
+               <!--
+                       <property name="ldifFilters"> <list> <bean
+                       class="org.apache.directory.server.protocol.shared.store.Krb5KdcEntryFilter"
+                       /> </list> </property>
+               -->
+
+
+               <!--
+                       the number of milliseconds before issuing a synch (flush to disk)
+               -->
+               <!--
+                       which writes out dirty pages back to disk. To turn off synchs all
+               -->
+               <!--
+                       together simply set this value to <= 0. Make sure you turn on
+               -->
+               <!--
+                       synchOnWrite for all partitions if you do choose to do this or else
+               -->
+               <!--
+                       writes may never persist to disk.
+               -->
+               <property name="synchPeriodMillis" value="15000" />
+
+               <!--
+                       limits searches by non-admin users to a max time of 15000
+               -->
+               <!--
+                       milliseconds and has a default value of 10000
+               -->
+               <property name="maxTimeLimit" value="15000" />
+               <!--
+                       limits searches to max size of 1000 entries: default value is 100
+               -->
+               <property name="maxSizeLimit" value="1000" />
+               <!--
+                       maximum number of threads used by mina is set to 8: default is 4
+               -->
+               <property name="maxThreads" value="8" />
+
+               <property name="allowAnonymousAccess" value="true" />
+               <property name="accessControlEnabled" value="false" />
+               <property name="enableNtp" value="false" />
+               <property name="enableKerberos" value="false" />
+               <property name="enableChangePassword" value="false" />
+
+               <!--
+                       It's more efficient to keep this feature turned off but you may not
+                       like having the creatorsName and modifiersName contain OIDs instead
+                       of short attributeType names instead. So if you want the creatorsName
+                       to change from the normalized form which is the internal
+                       representation of '0.9.2342.19200300.100.1.1=admin,2.5.4.11=system'
+
+                       to a more human readabile form like: 'uid=admin,ou=system' then set
+                       this property to true.
+               -->
+               <property name="denormalizeOpAttrsEnabled" value="false" />
+
+               <property name="ldapPort" value="${argeo.ldap.port}" />
+
+               <property name="systemPartitionConfiguration" ref="systemPartitionConfiguration" />
+
+               <property name="contextPartitionConfigurations">
+                       <set>
+                               <ref bean="examplePartitionConfiguration" />
+                       </set>
+               </property>
+               <property name="bootstrapSchemas">
+                       <set>
+                               <bean
+                                       class="org.apache.directory.server.core.schema.bootstrap.AutofsSchema" />
+                               <bean
+                                       class="org.apache.directory.server.core.schema.bootstrap.CorbaSchema" />
+                               <bean class="org.apache.directory.server.core.schema.bootstrap.CoreSchema" />
+                               <bean
+                                       class="org.apache.directory.server.core.schema.bootstrap.CosineSchema" />
+                               <bean
+                                       class="org.apache.directory.server.core.schema.bootstrap.ApacheSchema" />
+                               <bean
+                                       class="org.apache.directory.server.core.schema.bootstrap.CollectiveSchema" />
+                               <bean
+                                       class="org.apache.directory.server.core.schema.bootstrap.InetorgpersonSchema" />
+                               <bean class="org.apache.directory.server.core.schema.bootstrap.JavaSchema" />
+                               <bean
+                                       class="org.apache.directory.server.core.schema.bootstrap.Krb5kdcSchema" />
+                               <bean class="org.apache.directory.server.core.schema.bootstrap.NisSchema" />
+                               <bean
+                                       class="org.apache.directory.server.core.schema.bootstrap.SystemSchema" />
+                               <bean
+                                       class="org.apache.directory.server.core.schema.bootstrap.ApachednsSchema" />
+                       </set>
+               </property>
+
+               <property name="extendedOperationHandlers">
+                       <list>
+                               <bean
+                                       class="org.apache.directory.server.ldap.support.extended.GracefulShutdownHandler" />
+                               <bean
+                                       class="org.apache.directory.server.ldap.support.extended.LaunchDiagnosticUiHandler" />
+                       </list>
+               </property>
+
+               <property name="interceptorConfigurations">
+                       <list>
+                               <bean
+                                       class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
+                                       <property name="name" value="normalizationService" />
+                                       <property name="interceptor">
+                                               <bean
+                                                       class="org.apache.directory.server.core.normalization.NormalizationService" />
+                                       </property>
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
+                                       <property name="name" value="authenticationService" />
+                                       <property name="interceptor">
+                                               <bean
+                                                       class="org.apache.directory.server.core.authn.AuthenticationService" />
+                                       </property>
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
+                                       <property name="name" value="referralService" />
+                                       <property name="interceptor">
+                                               <bean class="org.apache.directory.server.core.referral.ReferralService" />
+                                       </property>
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
+                                       <property name="name" value="authorizationService" />
+                                       <property name="interceptor">
+                                               <bean
+                                                       class="org.apache.directory.server.core.authz.AuthorizationService" />
+                                       </property>
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
+                                       <property name="name" value="defaultAuthorizationService" />
+                                       <property name="interceptor">
+                                               <bean
+                                                       class="org.apache.directory.server.core.authz.DefaultAuthorizationService" />
+                                       </property>
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
+                                       <property name="name" value="exceptionService" />
+                                       <property name="interceptor">
+                                               <bean
+                                                       class="org.apache.directory.server.core.exception.ExceptionService" />
+                                       </property>
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
+                                       <property name="name" value="schemaService" />
+                                       <property name="interceptor">
+                                               <bean class="org.apache.directory.server.core.schema.SchemaService" />
+                                       </property>
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
+                                       <property name="name" value="subentryService" />
+                                       <property name="interceptor">
+                                               <bean class="org.apache.directory.server.core.subtree.SubentryService" />
+                                       </property>
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
+                                       <property name="name" value="operationalAttributeService" />
+                                       <property name="interceptor">
+                                               <bean
+                                                       class="org.apache.directory.server.core.operational.OperationalAttributeService" />
+                                       </property>
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
+                                       <property name="name" value="collectiveAttributeService" />
+                                       <property name="interceptor">
+                                               <bean
+                                                       class="org.apache.directory.server.core.collective.CollectiveAttributeService" />
+                                       </property>
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.configuration.MutableInterceptorConfiguration">
+                                       <property name="name" value="eventService" />
+                                       <property name="interceptor">
+                                               <bean class="org.apache.directory.server.core.event.EventService" />
+                                       </property>
+                               </bean>
+                       </list>
+               </property>
+       </bean>
+
+       <!--
+               use the following partitionConfiguration to override defaults for
+       -->
+       <!--
+               the system partition
+       -->
+       <bean id="systemPartitionConfiguration"
+               class="org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration">
+               <property name="name" value="system" />
+               <property name="cacheSize" value="100" />
+               <property name="suffix" value="ou=system" />
+
+               <!--
+                       the optimizer is enabled by default but may not always be what
+               -->
+               <!--
+                       you want if your queries are really simple
+               -->
+               <property name="optimizerEnabled" value="true" />
+
+               <!--
+                       Synchronization on writes does not wait for synch operations to flush
+                       dirty pages. Writes persist immediately to disk at a cost to
+                       performance with increased data integrity. Otherwise the periodic
+                       synch operation will flush dirty pages using the synchPeriodMillis
+                       parameter in the main configuration.
+               -->
+               <property name="synchOnWrite" value="true" />
+               <property name="indexedAttributes">
+                       <set>
+                               <bean
+                                       class="org.apache.directory.server.core.partition.impl.btree.MutableIndexConfiguration">
+                                       <property name="attributeId" value="ou" />
+                                       <property name="cacheSize" value="100" />
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.partition.impl.btree.MutableIndexConfiguration">
+                                       <property name="attributeId" value="uid" />
+                                       <property name="cacheSize" value="100" />
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.partition.impl.btree.MutableIndexConfiguration">
+                                       <property name="attributeId" value="objectClass" />
+                                       <property name="cacheSize" value="100" />
+                               </bean>
+                       </set>
+               </property>
+               <property name="contextEntry">
+                       <value><![CDATA[
+                               objectClass: top
+                               objectClass: organizationalUnit
+                               objectClass:extensibleObject
+                               ou: system
+                               ]]></value>
+               </property>
+       </bean>
+
+
+       <bean id="examplePartitionConfiguration"
+               class="org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration">
+               <property name="name" value="example" />
+               <property name="cacheSize" value="100" />
+               <property name="suffix" value="${argeo.ldap.rootdn}" />
+
+               <!--
+                       the optimizer is enabled by default but may not always be what
+               -->
+               <!--
+                       you want if your queries are really simple
+               -->
+               <property name="optimizerEnabled" value="true" />
+
+               <!--
+                       Synchronization on writes does not wait for synch operations to flush
+                       dirty pages. Writes persist immediately to disk at a cost to
+                       performance with increased data integrity. Otherwise the periodic
+                       synch operation will flush dirty pages using the synchPeriodMillis
+                       parameter in the main configuration.
+               -->
+               <property name="synchOnWrite" value="true" />
+               <property name="indexedAttributes">
+                       <set>
+                               <bean
+                                       class="org.apache.directory.server.core.partition.impl.btree.MutableIndexConfiguration">
+                                       <property name="attributeId" value="dc" />
+                                       <property name="cacheSize" value="100" />
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.partition.impl.btree.MutableIndexConfiguration">
+                                       <property name="attributeId" value="ou" />
+                                       <property name="cacheSize" value="100" />
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.partition.impl.btree.MutableIndexConfiguration">
+                                       <property name="attributeId" value="krb5PrincipalName" />
+                                       <property name="cacheSize" value="100" />
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.partition.impl.btree.MutableIndexConfiguration">
+                                       <property name="attributeId" value="uid" />
+                                       <property name="cacheSize" value="100" />
+                               </bean>
+                               <bean
+                                       class="org.apache.directory.server.core.partition.impl.btree.MutableIndexConfiguration">
+                                       <property name="attributeId" value="objectClass" />
+                                       <property name="cacheSize" value="100" />
+                               </bean>
+                       </set>
+               </property>
+               <property name="contextEntry">
+                       <value><![CDATA[
+                               objectClass: top
+                               objectClass: domain
+                               objectClass: extensibleObject
+                               dc: example
+                               ]]></value>
+               </property>
+       </bean>
+
+       <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
+               <property name="customEditors">
+                       <map>
+                               <entry key="javax.naming.directory.Attributes">
+                                       <bean
+                                               class="org.apache.directory.server.core.configuration.AttributesPropertyEditor" />
+                               </entry>
+                       </map>
+               </property>
+       </bean>
+</beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.ads.server/ads.properties b/trunk/server/modules/org.argeo.server.ads.server/ads.properties
new file mode 100644 (file)
index 0000000..4aa138d
--- /dev/null
@@ -0,0 +1,5 @@
+argeo.ldap.rootdn=dc=demo,dc=example,dc=org
+argeo.ldap.port=10389
+argeo.ldap.manager.userdn=uid=admin,ou=system
+argeo.ldap.manager.password=secret
+argeo.ads.init.ldif=osgibundle:init.ldif
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.ads.server/build.properties b/trunk/server/modules/org.argeo.server.ads.server/build.properties
new file mode 100644 (file)
index 0000000..5f22cdd
--- /dev/null
@@ -0,0 +1 @@
+bin.includes = META-INF/
diff --git a/trunk/server/modules/org.argeo.server.ads.server/init.ldif b/trunk/server/modules/org.argeo.server.ads.server/init.ldif
new file mode 100644 (file)
index 0000000..0d2e8ba
--- /dev/null
@@ -0,0 +1,47 @@
+dn: dc=demo,dc=example,dc=org
+objectClass: domain
+objectClass: extensibleObject
+objectClass: top
+dc: demo
+
+dn: ou=Roles,dc=demo,dc=example,dc=org
+objectClass: organizationalUnit
+objectClass: top
+ou: Roles
+
+dn: ou=People,dc=demo,dc=example,dc=org
+objectClass: organizationalUnit
+objectClass: top
+ou: People
+
+dn: uid=demo,ou=People,dc=demo,dc=example,dc=org
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: demo User
+description: Demo user
+givenname: Demo
+mail: demo@localhost
+sn: User
+uid: demo
+userpassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
+
+dn: uid=root,ou=People,dc=demo,dc=example,dc=org
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: top
+cn: demo User
+description: Superuser
+givenname: Root
+mail: root@localhost
+sn: Root
+uid: root
+userpassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9
+
+dn: cn=admin,ou=Roles,dc=demo,dc=example,dc=org
+objectClass: groupOfNames
+objectClass: top
+cn: admin
+member: uid=root,ou=People,dc=demo,dc=example,dc=org
diff --git a/trunk/server/modules/org.argeo.server.ads.server/pom.xml b/trunk/server/modules/org.argeo.server.ads.server/pom.xml
new file mode 100644 (file)
index 0000000..7cdd16d
--- /dev/null
@@ -0,0 +1,50 @@
+<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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.ads.server</artifactId>
+       <name>Commons Server Default ADS LDAP Server</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <!-- Import-Package needs to be specified explicitly since BND doesn't 
+                                                       deal with custom Spring-Context instructions -->
+                                               <Spring-Context>META-INF/spring/*.xml;create-asynchronously:=false</Spring-Context>
+                                               <Import-Package>
+                                                       *,
+                                                       javax.naming.directory,
+                                                       jdbm,
+                                                       org.apache.directory.server.configuration,
+                                                       org.apache.directory.server.core.authn,
+                                                       org.apache.directory.server.core.authz,
+                                                       org.apache.directory.server.core.collective,
+                                                       org.apache.directory.server.core.configuration,
+                                                       org.apache.directory.server.core.event,
+                                                       org.apache.directory.server.core.exception,
+                                                       org.apache.directory.server.core.normalization,
+                                                       org.apache.directory.server.core.operational,
+                                                       org.apache.directory.server.core.partition.impl.btree,
+                                                       org.apache.directory.server.core.referral,
+                                                       org.apache.directory.server.core.schema,
+                                                       org.apache.directory.server.core.schema.bootstrap,
+                                                       org.apache.directory.server.core.subtree,
+                                                       org.apache.directory.server.jndi,
+                                                       org.apache.directory.server.ldap.support.extended,
+                                                       org.apache.directory.shared.asn1.codec,
+                                                       org.argeo.server.ads,
+                                                       org.springframework.beans.factory.config
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.catalina/conf/catalina.policy b/trunk/server/modules/org.argeo.server.catalina/conf/catalina.policy
new file mode 100644 (file)
index 0000000..5a6426e
--- /dev/null
@@ -0,0 +1,178 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// ============================================================================
+// catalina.corepolicy - Security Policy Permissions for Tomcat 6
+//
+// This file contains a default set of security policies to be enforced (by the
+// JVM) when Catalina is executed with the "-security" option.  In addition
+// to the permissions granted here, the following additional permissions are
+// granted to the codebase specific to each web application:
+//
+// * Read access to the document root directory
+//
+// $Id: catalina.policy 648343 2008-04-15 17:21:29Z markt $
+// ============================================================================
+
+
+// ========== SYSTEM CODE PERMISSIONS =========================================
+
+
+// These permissions apply to javac
+grant codeBase "file:${java.home}/lib/-" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to all shared system extensions
+grant codeBase "file:${java.home}/jre/lib/ext/-" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to javac when ${java.home] points at $JAVA_HOME/jre
+grant codeBase "file:${java.home}/../lib/-" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to all shared system extensions when
+// ${java.home} points at $JAVA_HOME/jre
+grant codeBase "file:${java.home}/lib/ext/-" {
+        permission java.security.AllPermission;
+};
+
+
+// ========== CATALINA CODE PERMISSIONS =======================================
+
+
+// These permissions apply to the daemon code
+grant codeBase "file:${catalina.home}/bin/commons-daemon.jar" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to the logging API
+grant codeBase "file:${catalina.home}/bin/tomcat-juli.jar" {
+        permission java.util.PropertyPermission "java.util.logging.config.class", "read";
+        permission java.util.PropertyPermission "java.util.logging.config.file", "read";
+        permission java.io.FilePermission "${java.home}${file.separator}lib${file.separator}logging.properties", "read"; 
+        permission java.lang.RuntimePermission "shutdownHooks";
+        permission java.io.FilePermission "${catalina.base}${file.separator}conf${file.separator}logging.properties", "read";
+        permission java.util.PropertyPermission "catalina.base", "read";
+        permission java.util.logging.LoggingPermission "control";
+        permission java.io.FilePermission "${catalina.base}${file.separator}logs", "read, write";
+        permission java.io.FilePermission "${catalina.base}${file.separator}logs${file.separator}*", "read, write";
+        permission java.lang.RuntimePermission "getClassLoader";
+        // To enable per context logging configuration, permit read access to the appropriate file.
+        // Be sure that the logging configuration is secure before enabling such access
+        // eg for the examples web application:
+        // permission java.io.FilePermission "${catalina.base}${file.separator}webapps${file.separator}examples${file.separator}WEB-INF${file.separator}classes${file.separator}logging.properties", "read";
+};
+
+// These permissions apply to the server startup code
+grant codeBase "file:${catalina.home}/bin/bootstrap.jar" {
+        permission java.security.AllPermission;
+};
+
+// These permissions apply to the servlet API classes
+// and those that are shared across all class loaders
+// located in the "lib" directory
+grant codeBase "file:${catalina.home}/lib/-" {
+        permission java.security.AllPermission;
+};
+
+
+// ========== WEB APPLICATION PERMISSIONS =====================================
+
+
+// These permissions are granted by default to all web applications
+// In addition, a web application will be given a read FilePermission
+// and JndiPermission for all files and directories in its document root.
+grant { 
+    // Required for JNDI lookup of named JDBC DataSource's and
+    // javamail named MimePart DataSource used to send mail
+    permission java.util.PropertyPermission "java.home", "read";
+    permission java.util.PropertyPermission "java.naming.*", "read";
+    permission java.util.PropertyPermission "javax.sql.*", "read";
+
+    // OS Specific properties to allow read access
+    permission java.util.PropertyPermission "os.name", "read";
+    permission java.util.PropertyPermission "os.version", "read";
+    permission java.util.PropertyPermission "os.arch", "read";
+    permission java.util.PropertyPermission "file.separator", "read";
+    permission java.util.PropertyPermission "path.separator", "read";
+    permission java.util.PropertyPermission "line.separator", "read";
+
+    // JVM properties to allow read access
+    permission java.util.PropertyPermission "java.version", "read";
+    permission java.util.PropertyPermission "java.vendor", "read";
+    permission java.util.PropertyPermission "java.vendor.url", "read";
+    permission java.util.PropertyPermission "java.class.version", "read";
+    permission java.util.PropertyPermission "java.specification.version", "read";
+    permission java.util.PropertyPermission "java.specification.vendor", "read";
+    permission java.util.PropertyPermission "java.specification.name", "read";
+
+    permission java.util.PropertyPermission "java.vm.specification.version", "read";
+    permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
+    permission java.util.PropertyPermission "java.vm.specification.name", "read";
+    permission java.util.PropertyPermission "java.vm.version", "read";
+    permission java.util.PropertyPermission "java.vm.vendor", "read";
+    permission java.util.PropertyPermission "java.vm.name", "read";
+
+    // Required for OpenJMX
+    permission java.lang.RuntimePermission "getAttribute";
+
+    // Allow read of JAXP compliant XML parser debug
+    permission java.util.PropertyPermission "jaxp.debug", "read";
+
+    // Precompiled JSPs need access to this package.
+    permission java.lang.RuntimePermission "accessClassInPackage.org.apache.jasper.runtime";
+    permission java.lang.RuntimePermission "accessClassInPackage.org.apache.jasper.runtime.*";
+    
+    // Precompiled JSPs need access to this system property.
+    permission java.util.PropertyPermission "org.apache.jasper.runtime.BodyContentImpl.LIMIT_BUFFER", "read";
+
+};
+
+
+// You can assign additional permissions to particular web applications by
+// adding additional "grant" entries here, based on the code base for that
+// application, /WEB-INF/classes/, or /WEB-INF/lib/ jar files.
+//
+// Different permissions can be granted to JSP pages, classes loaded from
+// the /WEB-INF/classes/ directory, all jar files in the /WEB-INF/lib/
+// directory, or even to individual jar files in the /WEB-INF/lib/ directory.
+//
+// For instance, assume that the standard "examples" application
+// included a JDBC driver that needed to establish a network connection to the
+// corresponding database and used the scrape taglib to get the weather from
+// the NOAA web server.  You might create a "grant" entries like this:
+//
+// The permissions granted to the context root directory apply to JSP pages.
+// grant codeBase "file:${catalina.home}/webapps/examples/-" {
+//      permission java.net.SocketPermission "dbhost.mycompany.com:5432", "connect";
+//      permission java.net.SocketPermission "*.noaa.gov:80", "connect";
+// };
+//
+// The permissions granted to the context WEB-INF/classes directory
+// grant codeBase "file:${catalina.home}/webapps/examples/WEB-INF/classes/-" {
+// };
+//
+// The permission granted to your JDBC driver
+// grant codeBase "jar:file:${catalina.home}/webapps/examples/WEB-INF/lib/driver.jar!/-" {
+//      permission java.net.SocketPermission "dbhost.mycompany.com:5432", "connect";
+// };
+// The permission granted to the scrape taglib
+// grant codeBase "jar:file:${catalina.home}/webapps/examples/WEB-INF/lib/scrape.jar!/-" {
+//      permission java.net.SocketPermission "*.noaa.gov:80", "connect";
+// };
+
diff --git a/trunk/server/modules/org.argeo.server.catalina/conf/catalina.properties b/trunk/server/modules/org.argeo.server.catalina/conf/catalina.properties
new file mode 100644 (file)
index 0000000..00d758a
--- /dev/null
@@ -0,0 +1,81 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# List of comma-separated packages that start with or equal this string
+# will cause a security exception to be thrown when
+# passed to checkPackageAccess unless the
+# corresponding RuntimePermission ("accessClassInPackage."+package) has
+# been granted.
+package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.,sun.beans.
+#
+# List of comma-separated packages that start with or equal this string
+# will cause a security exception to be thrown when
+# passed to checkPackageDefinition unless the
+# corresponding RuntimePermission ("defineClassInPackage."+package) has
+# been granted.
+#
+# by default, no packages are restricted for definition, and none of
+# the class loaders supplied with the JDK call checkPackageDefinition.
+#
+package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.
+
+#
+#
+# List of comma-separated paths defining the contents of the "common" 
+# classloader. Prefixes should be used to define what is the repository type.
+# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
+# If left as blank,the JVM system loader will be used as Catalina's "common" 
+# loader.
+# Examples:
+#     "foo": Add this folder as a class repository
+#     "foo/*.jar": Add all the JARs of the specified folder as class 
+#                  repositories
+#     "foo/bar.jar": Add bar.jar as a class repository
+common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
+
+#
+# List of comma-separated paths defining the contents of the "server" 
+# classloader. Prefixes should be used to define what is the repository type.
+# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
+# If left as blank, the "common" loader will be used as Catalina's "server" 
+# loader.
+# Examples:
+#     "foo": Add this folder as a class repository
+#     "foo/*.jar": Add all the JARs of the specified folder as class 
+#                  repositories
+#     "foo/bar.jar": Add bar.jar as a class repository
+server.loader=
+
+#
+# List of comma-separated paths defining the contents of the "shared" 
+# classloader. Prefixes should be used to define what is the repository type.
+# Path may be relative to the CATALINA_BASE path or absolute. If left as blank,
+# the "common" loader will be used as Catalina's "shared" loader.
+# Examples:
+#     "foo": Add this folder as a class repository
+#     "foo/*.jar": Add all the JARs of the specified folder as class 
+#                  repositories
+#     "foo/bar.jar": Add bar.jar as a class repository 
+# Please note that for single jars, e.g. bar.jar, you need the URL form
+# starting with file:.
+shared.loader=
+
+#
+# String cache configuration.
+tomcat.util.buf.StringCache.byte.enabled=true
+#tomcat.util.buf.StringCache.char.enabled=true
+#tomcat.util.buf.StringCache.trainThreshold=500000
+#tomcat.util.buf.StringCache.cacheSize=5000
diff --git a/trunk/server/modules/org.argeo.server.catalina/conf/context.xml b/trunk/server/modules/org.argeo.server.catalina/conf/context.xml
new file mode 100644 (file)
index 0000000..8eb2d2b
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!-- The contents of this file will be loaded for each web application -->
+<Context>
+
+    <!-- Default set of monitored resources -->
+    <WatchedResource>WEB-INF/web.xml</WatchedResource>
+       
+    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
+    <!--
+    <Manager pathname="" />
+    -->
+
+    <!-- Uncomment this to enable Comet connection tacking (provides events
+         on session expiration as well as webapp lifecycle) -->
+    <!--
+    <Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
+    -->
+
+</Context>
diff --git a/trunk/server/modules/org.argeo.server.catalina/conf/web.xml b/trunk/server/modules/org.argeo.server.catalina/conf/web.xml
new file mode 100644 (file)
index 0000000..88e9f49
--- /dev/null
@@ -0,0 +1,1186 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+    version="2.5">
+
+  <!-- ======================== Introduction ============================== -->
+  <!-- This document defines default values for *all* web applications      -->
+  <!-- loaded into this instance of Tomcat.  As each application is         -->
+  <!-- deployed, this file is processed, followed by the                    -->
+  <!-- "/WEB-INF/web.xml" deployment descriptor from your own               -->
+  <!-- applications.                                                        -->
+  <!--                                                                      -->
+  <!-- WARNING:  Do not configure application-specific resources here!      -->
+  <!-- They should go in the "/WEB-INF/web.xml" file in your application.   -->
+
+
+  <!-- ================== Built In Servlet Definitions ==================== -->
+
+
+  <!-- The default servlet for all web applications, that serves static     -->
+  <!-- resources.  It processes all requests that are not mapped to other   -->
+  <!-- servlets with servlet mappings (defined either here or in your own   -->
+  <!-- web.xml file.  This servlet supports the following initialization    -->
+  <!-- parameters (default values are in square brackets):                  -->
+  <!--                                                                      -->
+  <!--   debug               Debugging detail level for messages logged     -->
+  <!--                       by this servlet.  [0]                          -->
+  <!--                                                                      -->
+  <!--   fileEncoding        Encoding to be used to read static resources   -->
+  <!--                       [platform default]                             -->
+  <!--                                                                      -->
+  <!--   input               Input buffer size (in bytes) when reading      -->
+  <!--                       resources to be served.  [2048]                -->
+  <!--                                                                      -->
+  <!--   listings            Should directory listings be produced if there -->
+  <!--                       is no welcome file in this directory?  [false] -->
+  <!--                       WARNING: Listings for directories with many    -->
+  <!--                       entries can be slow and may consume            -->
+  <!--                       significant proportions of server resources.   -->
+  <!--                                                                      -->
+  <!--   output              Output buffer size (in bytes) when writing     -->
+  <!--                       resources to be served.  [2048]                -->
+  <!--                                                                      -->
+  <!--   readonly            Is this context "read only", so HTTP           -->
+  <!--                       commands like PUT and DELETE are               -->
+  <!--                       rejected?  [true]                              -->
+  <!--                                                                      -->
+  <!--   readmeFile          File name to display with the directory        -->
+  <!--                       contents. [null]                               -->
+  <!--                                                                      -->
+  <!--   sendfileSize        If the connector used supports sendfile, this  -->
+  <!--                       represents the minimal file size in KB for     -->
+  <!--                       which sendfile will be used. Use a negative    -->
+  <!--                       value to always disable sendfile.  [48]        -->
+  <!--                                                                      -->
+  <!--  For directory listing customization. Checks localXsltFile, then     -->
+  <!--  globalXsltFile, then defaults to original behavior.                 -->
+  <!--                                                                      -->
+  <!--   localXsltFile       Make directory listings an XML doc and         -->
+  <!--                       pass the result to this style sheet residing   -->
+  <!--                       in that directory. This overrides              -->
+  <!--                        globalXsltFile[null]                          -->
+  <!--                                                                      -->
+  <!--   globalXsltFile      Site wide configuration version of             -->
+  <!--                       localXsltFile This argument is expected        -->
+  <!--                       to be a physical file. [null]                  -->
+  <!--                                                                      -->
+  <!--                                                                      -->
+
+    <servlet>
+        <servlet-name>default</servlet-name>
+        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
+        <init-param>
+            <param-name>debug</param-name>
+            <param-value>0</param-value>
+        </init-param>
+        <init-param>
+            <param-name>listings</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+
+  <!-- The "invoker" servlet, which executes anonymous servlet classes      -->
+  <!-- that have not been defined in a web.xml file.  Traditionally, this   -->
+  <!-- servlet is mapped to the URL pattern "/servlet/*", but you can map   -->
+  <!-- it to other patterns as well.  The extra path info portion of such a -->
+  <!-- request must be the fully qualified class name of a Java class that  -->
+  <!-- implements Servlet (or extends HttpServlet), or the servlet name     -->
+  <!-- of an existing servlet definition.     This servlet supports the     -->
+  <!-- following initialization parameters (default values are in square    -->
+  <!-- brackets):                                                           -->
+  <!--                                                                      -->
+  <!--   debug               Debugging detail level for messages logged     -->
+  <!--                       by this servlet.  [0]                          -->
+
+<!--
+    <servlet>
+        <servlet-name>invoker</servlet-name>
+        <servlet-class>
+          org.apache.catalina.servlets.InvokerServlet
+        </servlet-class>
+        <init-param>
+            <param-name>debug</param-name>
+            <param-value>0</param-value>
+        </init-param>
+        <load-on-startup>2</load-on-startup>
+    </servlet>
+-->
+
+
+  <!-- The JSP page compiler and execution servlet, which is the mechanism  -->
+  <!-- used by Tomcat to support JSP pages.  Traditionally, this servlet    -->
+  <!-- is mapped to the URL pattern "*.jsp".  This servlet supports the     -->
+  <!-- following initialization parameters (default values are in square    -->
+  <!-- brackets):                                                           -->
+  <!--                                                                      -->
+  <!--   checkInterval       If development is false and checkInterval is   -->
+  <!--                       greater than zero, background compilations are -->
+  <!--                       enabled. checkInterval is the time in seconds  -->
+  <!--                       between checks to see if a JSP page (and its   -->
+  <!--                       dependent files) needs to  be recompiled. [0]  -->
+  <!--                                                                      -->
+  <!--   classdebuginfo      Should the class file be compiled with         -->
+  <!--                       debugging information?  [true]                 -->
+  <!--                                                                      -->
+  <!--   classpath           What class path should I use while compiling   -->
+  <!--                       generated servlets?  [Created dynamically      -->
+  <!--                       based on the current web application]          -->
+  <!--                                                                      -->
+  <!--   compiler            Which compiler Ant should use to compile JSP   -->
+  <!--                       pages.  See the jasper documentation for more  -->
+  <!--                       information.                                   -->
+  <!--                                                                      -->
+  <!--   compilerSourceVM    Compiler source VM                             -->
+  <!--                       default is System.properties                   -->
+  <!--                        java.specification.version > 1.4              -->
+  <!--                        [1.5] else [1.4]                              -->
+  <!--                                                                      -->
+  <!--   compilerTargetVM    Compiler target VM                             -->  
+  <!--                       default is System.properties                   -->
+  <!--                        java.specification.version > 1.4              -->
+  <!--                        [1.5] else [1.4]                              -->
+  <!--                                                                      -->
+  <!--   development         Is Jasper used in development mode? If true,   -->
+  <!--                       the frequency at which JSPs are checked for    -->
+  <!--                       modification may be specified via the          -->
+  <!--                       modificationTestInterval parameter. [true]     -->
+  <!--                                                                      -->
+  <!--   displaySourceFragment                                              -->
+  <!--                       Should a source fragment be included in        -->
+  <!--                       exception messages? [true]                     -->
+  <!--                                                                      -->
+  <!--   dumpSmap            Should the SMAP info for JSR45 debugging be    -->
+  <!--                       dumped to a file? [false]                      -->
+  <!--                       False if suppressSmap is true                  -->
+  <!--                                                                      -->
+  <!--   enablePooling       Determines whether tag handler pooling is      -->
+  <!--                       enabled  [true]                                -->
+  <!--                                                                      -->
+  <!--   engineOptionsClass  Allows specifying the Options class used to    -->
+  <!--                       configure Jasper. If not present, the default  -->
+  <!--                       EmbeddedServletOptions will be used.           -->
+  <!--                                                                      -->
+  <!--   errorOnUseBeanInvalidClassAttribute                                -->
+  <!--                       Should Jasper issue an error when the value of -->
+  <!--                       the class attribute in an useBean action is    -->
+  <!--                       not a valid bean class?  [true]                -->
+  <!--                                                                      -->
+  <!--   fork                Tell Ant to fork compiles of JSP pages so that -->
+  <!--                       a separate JVM is used for JSP page compiles   -->
+  <!--                       from the one Tomcat is running in. [true]      -->
+  <!--                                                                      -->
+  <!--   genStrAsCharArray   Should text strings be generated as char       -->
+  <!--                       arrays, to improve performance in some cases?  -->
+  <!--                       [false]                                        -->
+  <!--                                                                      -->
+  <!--   ieClassId           The class-id value to be sent to Internet      -->
+  <!--                       Explorer when using <jsp:plugin> tags.         -->
+  <!--                       [clsid:8AD9C840-044E-11D1-B3E9-00805F499D93]   -->
+  <!--                                                                      -->
+  <!--   javaEncoding        Java file encoding to use for generating java  -->
+  <!--                       source files. [UTF8]                           -->
+  <!--                                                                      -->
+  <!--   keepgenerated       Should we keep the generated Java source code  -->
+  <!--                       for each page instead of deleting it? [true]   -->
+  <!--                                                                      -->
+  <!--   mappedfile          Should we generate static content with one     -->
+  <!--                       print statement per input line, to ease        -->
+  <!--                       debugging?  [true]                             -->
+  <!--                                                                      -->
+  <!--   modificationTestInterval                                           -->
+  <!--                       Causes a JSP (and its dependent files) to not  -->
+  <!--                       be checked for modification during the         -->
+  <!--                       specified time interval (in seconds) from the  -->
+  <!--                       last time the JSP was checked for              -->
+  <!--                       modification. A value of 0 will cause the JSP  -->
+  <!--                       to be checked on every access.                 -->
+  <!--                       Used in development mode only. [4]             -->
+  <!--                                                                      -->
+  <!--   scratchdir          What scratch directory should we use when      -->
+  <!--                       compiling JSP pages?  [default work directory  -->
+  <!--                       for the current web application]               -->
+  <!--                                                                      -->
+  <!--   suppressSmap        Should the generation of SMAP info for JSR45   -->
+  <!--                       debugging be suppressed?  [false]              -->
+  <!--                                                                      -->
+  <!--   trimSpaces          Should white spaces in template text between   -->
+  <!--                       actions or directives be trimmed?  [false]     -->
+  <!--                                                                      -->
+  <!--   xpoweredBy          Determines whether X-Powered-By response       -->
+  <!--                       header is added by generated servlet  [false]  -->
+  <!--                                                                      -->
+  <!-- If you wish to use Jikes to compile JSP pages:                       -->
+  <!--   Please see the "Using Jikes" section of the Jasper-HowTo           -->
+  <!--   page in the Tomcat documentation.                                  -->
+
+    <servlet>
+        <servlet-name>jsp</servlet-name>
+        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+        <init-param>
+            <param-name>fork</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <init-param>
+            <param-name>xpoweredBy</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <load-on-startup>3</load-on-startup>
+    </servlet>
+
+
+  <!-- NOTE: An SSI Filter is also available as an alternative SSI          -->
+  <!-- implementation. Use either the Servlet or the Filter but NOT both.   -->
+  <!--                                                                      -->
+  <!-- Server Side Includes processing servlet, which processes SSI         -->
+  <!-- directives in HTML pages consistent with similar support in web      -->
+  <!-- servers like Apache.  Traditionally, this servlet is mapped to the   -->
+  <!-- URL pattern "*.shtml".  This servlet supports the following          -->
+  <!-- initialization parameters (default values are in square brackets):   -->
+  <!--                                                                      -->
+  <!--   buffered            Should output from this servlet be buffered?   -->
+  <!--                       (0=false, 1=true)  [0]                         -->
+  <!--                                                                      -->
+  <!--   debug               Debugging detail level for messages logged     -->
+  <!--                       by this servlet.  [0]                          -->
+  <!--                                                                      -->
+  <!--   expires             The number of seconds before a page with SSI   -->
+  <!--                       directives will expire.  [No default]          -->
+  <!--                                                                      -->
+  <!--   isVirtualWebappRelative                                            -->
+  <!--                       Should "virtual" paths be interpreted as       -->
+  <!--                       relative to the context root, instead of       -->
+  <!--                       the server root?  (0=false, 1=true) [0]        -->
+  <!--                                                                      -->
+  <!--   inputEncoding       The encoding to assume for SSI resources if    -->
+  <!--                       one is not available from the resource.        -->
+  <!--                       [Platform default]                             -->
+  <!--                                                                      -->
+  <!--   outputEncoding      The encoding to use for the page that results  -->
+  <!--                       from the SSI processing. [UTF-8]               -->
+
+<!--
+    <servlet>
+        <servlet-name>ssi</servlet-name>
+        <servlet-class>
+          org.apache.catalina.ssi.SSIServlet
+        </servlet-class>
+        <init-param>
+          <param-name>buffered</param-name>
+          <param-value>1</param-value>
+        </init-param>
+        <init-param>
+          <param-name>debug</param-name>
+          <param-value>0</param-value>
+        </init-param>
+        <init-param>
+          <param-name>expires</param-name>
+          <param-value>666</param-value>
+        </init-param>
+        <init-param>
+          <param-name>isVirtualWebappRelative</param-name>
+          <param-value>0</param-value>
+        </init-param>
+        <load-on-startup>4</load-on-startup>
+    </servlet>
+-->
+
+
+  <!-- Common Gateway Includes (CGI) processing servlet, which supports     -->
+  <!-- execution of external applications that conform to the CGI spec      -->
+  <!-- requirements.  Typically, this servlet is mapped to the URL pattern  -->
+  <!-- "/cgi-bin/*", which means that any CGI applications that are         -->
+  <!-- executed must be present within the web application.  This servlet   -->
+  <!-- supports the following initialization parameters (default values     -->
+  <!-- are in square brackets):                                             -->
+  <!--                                                                      -->
+  <!--   cgiPathPrefix        The CGI search path will start at             -->
+  <!--                        webAppRootDir + File.separator + this prefix. -->
+  <!--                        [WEB-INF/cgi]                                 -->
+  <!--                                                                      -->
+  <!--   debug                Debugging detail level for messages logged    -->
+  <!--                        by this servlet.  [0]                         -->
+  <!--                                                                      -->
+  <!--   executable           Name of the exectuable used to run the        -->
+  <!--                        script. [perl]                                -->
+  <!--                                                                      -->
+  <!--   parameterEncoding    Name of parameter encoding to be used with    -->
+  <!--                        CGI servlet.                                  -->
+  <!--                        [System.getProperty("file.encoding","UTF-8")] -->
+  <!--                                                                      -->
+  <!--   passShellEnvironment Should the shell environment variables (if    -->
+  <!--                        any) be passed to the CGI script? [false]     -->
+
+<!--
+    <servlet>
+        <servlet-name>cgi</servlet-name>
+        <servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
+        <init-param>
+          <param-name>debug</param-name>
+          <param-value>0</param-value>
+        </init-param>
+        <init-param>
+          <param-name>cgiPathPrefix</param-name>
+          <param-value>WEB-INF/cgi</param-value>
+        </init-param>
+         <load-on-startup>5</load-on-startup>
+    </servlet>
+-->
+
+
+  <!-- ================ Built In Servlet Mappings ========================= -->
+
+
+  <!-- The servlet mappings for the built in servlets defined above.  Note  -->
+  <!-- that, by default, the CGI and SSI servlets are *not* mapped.  You    -->
+  <!-- must uncomment these mappings (or add them to your application's own -->
+  <!-- web.xml deployment descriptor) to enable these services              -->
+
+    <!-- The mapping for the default servlet -->
+    <servlet-mapping>
+        <servlet-name>default</servlet-name>
+        <url-pattern>/</url-pattern>
+    </servlet-mapping>
+
+    <!-- The mapping for the invoker servlet -->
+<!--
+    <servlet-mapping>
+        <servlet-name>invoker</servlet-name>
+        <url-pattern>/servlet/*</url-pattern>
+    </servlet-mapping>
+-->
+
+    <!-- The mapping for the JSP servlet -->
+    <servlet-mapping>
+        <servlet-name>jsp</servlet-name>
+        <url-pattern>*.jsp</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>jsp</servlet-name>
+        <url-pattern>*.jspx</url-pattern>
+    </servlet-mapping>
+
+    <!-- The mapping for the SSI servlet -->
+<!--
+    <servlet-mapping>
+        <servlet-name>ssi</servlet-name>
+        <url-pattern>*.shtml</url-pattern>
+    </servlet-mapping>
+-->
+
+    <!-- The mapping for the CGI Gateway servlet -->
+
+<!--
+    <servlet-mapping>
+        <servlet-name>cgi</servlet-name>
+        <url-pattern>/cgi-bin/*</url-pattern>
+    </servlet-mapping>
+-->
+
+
+  <!-- ================== Built In Filter Definitions ===================== -->
+
+  <!-- NOTE: An SSI Servlet is also available as an alternative SSI         -->
+  <!-- implementation. Use either the Servlet or the Filter but NOT both.   -->
+  <!--                                                                      -->
+  <!-- Server Side Includes processing filter, which processes SSI          -->
+  <!-- directives in HTML pages consistent with similar support in web      -->
+  <!-- servers like Apache.  Traditionally, this filter is mapped to the    -->
+  <!-- URL pattern "*.shtml", though it can be mapped to "*" as it will     -->
+  <!-- selectively enable/disable SSI processing based on mime types. For   -->
+  <!-- this to work you will need to uncomment the .shtml mime type         -->
+  <!-- definition towards the bottom of this file.                          -->
+  <!-- The contentType init param allows you to apply SSI processing to JSP -->
+  <!-- pages, javascript, or any other content you wish.  This filter       -->
+  <!-- supports the following initialization parameters (default values are -->
+  <!-- in square brackets):                                                 -->
+  <!--                                                                      -->
+  <!--   contentType         A regex pattern that must be matched before    -->
+  <!--                       SSI processing is applied.                     -->
+  <!--                       [text/x-server-parsed-html(;.*)?]              -->
+  <!--                                                                      -->
+  <!--   debug               Debugging detail level for messages logged     -->
+  <!--                       by this servlet.  [0]                          -->
+  <!--                                                                      -->
+  <!--   expires             The number of seconds before a page with SSI   -->
+  <!--                       directives will expire.  [No default]          -->
+  <!--                                                                      -->
+  <!--   isVirtualWebappRelative                                            -->
+  <!--                       Should "virtual" paths be interpreted as       -->
+  <!--                       relative to the context root, instead of       -->
+  <!--                       the server root?  (0=false, 1=true) [0]        -->
+
+<!--
+    <filter>
+        <filter-name>ssi</filter-name>
+        <filter-class>
+          org.apache.catalina.ssi.SSIFilter
+        </filter-class>
+        <init-param>
+          <param-name>contentType</param-name>
+          <param-value>text/x-server-parsed-html(;.*)?</param-value>
+        </init-param>
+        <init-param>
+          <param-name>debug</param-name>
+          <param-value>0</param-value>
+        </init-param>
+        <init-param>
+          <param-name>expires</param-name>
+          <param-value>666</param-value>
+        </init-param>
+        <init-param>
+          <param-name>isVirtualWebappRelative</param-name>
+          <param-value>0</param-value>
+        </init-param>
+    </filter>
+-->
+
+
+  <!-- ==================== Built In Filter Mappings ====================== -->
+
+  <!-- The mapping for the SSI Filter -->
+<!--
+    <filter-mapping>
+        <filter-name>ssi</filter-name>
+        <url-pattern>*.shtml</url-pattern>
+    </filter-mapping>
+-->
+
+
+  <!-- ==================== Default Session Configuration ================= -->
+  <!-- You can set the default session timeout (in minutes) for all newly   -->
+  <!-- created sessions by modifying the value below.                       -->
+
+    <session-config>
+        <session-timeout>30</session-timeout>
+    </session-config>
+
+
+  <!-- ===================== Default MIME Type Mappings =================== -->
+  <!-- When serving static resources, Tomcat will automatically generate    -->
+  <!-- a "Content-Type" header based on the resource's filename extension,  -->
+  <!-- based on these mappings.  Additional mappings can be added here (to  -->
+  <!-- apply to all web applications), or in your own application's web.xml -->
+  <!-- deployment descriptor.                                               -->
+
+    <mime-mapping>
+        <extension>abs</extension>
+        <mime-type>audio/x-mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ai</extension>
+        <mime-type>application/postscript</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>aif</extension>
+        <mime-type>audio/x-aiff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>aifc</extension>
+        <mime-type>audio/x-aiff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>aiff</extension>
+        <mime-type>audio/x-aiff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>aim</extension>
+        <mime-type>application/x-aim</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>art</extension>
+        <mime-type>image/x-jg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>asf</extension>
+        <mime-type>video/x-ms-asf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>asx</extension>
+        <mime-type>video/x-ms-asf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>au</extension>
+        <mime-type>audio/basic</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>avi</extension>
+        <mime-type>video/x-msvideo</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>avx</extension>
+        <mime-type>video/x-rad-screenplay</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>bcpio</extension>
+        <mime-type>application/x-bcpio</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>bin</extension>
+        <mime-type>application/octet-stream</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>bmp</extension>
+        <mime-type>image/bmp</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>body</extension>
+        <mime-type>text/html</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>cdf</extension>
+        <mime-type>application/x-cdf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>cer</extension>
+        <mime-type>application/x-x509-ca-cert</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>class</extension>
+        <mime-type>application/java</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>cpio</extension>
+        <mime-type>application/x-cpio</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>csh</extension>
+        <mime-type>application/x-csh</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>css</extension>
+        <mime-type>text/css</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>dib</extension>
+        <mime-type>image/bmp</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>doc</extension>
+        <mime-type>application/msword</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>dtd</extension>
+        <mime-type>application/xml-dtd</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>dv</extension>
+        <mime-type>video/x-dv</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>dvi</extension>
+        <mime-type>application/x-dvi</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>eps</extension>
+        <mime-type>application/postscript</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>etx</extension>
+        <mime-type>text/x-setext</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>exe</extension>
+        <mime-type>application/octet-stream</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>gif</extension>
+        <mime-type>image/gif</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>gtar</extension>
+        <mime-type>application/x-gtar</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>gz</extension>
+        <mime-type>application/x-gzip</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>hdf</extension>
+        <mime-type>application/x-hdf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>hqx</extension>
+        <mime-type>application/mac-binhex40</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>htc</extension>
+        <mime-type>text/x-component</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>htm</extension>
+        <mime-type>text/html</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>html</extension>
+        <mime-type>text/html</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>hqx</extension>
+        <mime-type>application/mac-binhex40</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ief</extension>
+        <mime-type>image/ief</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jad</extension>
+        <mime-type>text/vnd.sun.j2me.app-descriptor</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jar</extension>
+        <mime-type>application/java-archive</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>java</extension>
+        <mime-type>text/plain</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jnlp</extension>
+        <mime-type>application/x-java-jnlp-file</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jpe</extension>
+        <mime-type>image/jpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jpeg</extension>
+        <mime-type>image/jpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jpg</extension>
+        <mime-type>image/jpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>js</extension>
+        <mime-type>text/javascript</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jsf</extension>
+        <mime-type>text/plain</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>jspf</extension>
+        <mime-type>text/plain</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>kar</extension>
+        <mime-type>audio/x-midi</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>latex</extension>
+        <mime-type>application/x-latex</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>m3u</extension>
+        <mime-type>audio/x-mpegurl</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mac</extension>
+        <mime-type>image/x-macpaint</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>man</extension>
+        <mime-type>application/x-troff-man</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mathml</extension>
+        <mime-type>application/mathml+xml</mime-type> 
+    </mime-mapping>
+    <mime-mapping>
+        <extension>me</extension>
+        <mime-type>application/x-troff-me</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mid</extension>
+        <mime-type>audio/x-midi</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>midi</extension>
+        <mime-type>audio/x-midi</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mif</extension>
+        <mime-type>application/x-mif</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mov</extension>
+        <mime-type>video/quicktime</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>movie</extension>
+        <mime-type>video/x-sgi-movie</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mp1</extension>
+        <mime-type>audio/x-mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mp2</extension>
+        <mime-type>audio/x-mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mp3</extension>
+        <mime-type>audio/x-mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mp4</extension>
+        <mime-type>video/mp4</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mpa</extension>
+        <mime-type>audio/x-mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mpe</extension>
+        <mime-type>video/mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mpeg</extension>
+        <mime-type>video/mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mpega</extension>
+        <mime-type>audio/x-mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mpg</extension>
+        <mime-type>video/mpeg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>mpv2</extension>
+        <mime-type>video/mpeg2</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ms</extension>
+        <mime-type>application/x-wais-source</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>nc</extension>
+        <mime-type>application/x-netcdf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>oda</extension>
+        <mime-type>application/oda</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Database -->
+        <extension>odb</extension>
+        <mime-type>application/vnd.oasis.opendocument.database</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Chart -->
+        <extension>odc</extension>
+        <mime-type>application/vnd.oasis.opendocument.chart</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Formula -->
+        <extension>odf</extension>
+        <mime-type>application/vnd.oasis.opendocument.formula</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Drawing -->
+        <extension>odg</extension>
+        <mime-type>application/vnd.oasis.opendocument.graphics</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Image -->
+        <extension>odi</extension>
+        <mime-type>application/vnd.oasis.opendocument.image</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Master Document -->
+        <extension>odm</extension>
+        <mime-type>application/vnd.oasis.opendocument.text-master</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Presentation -->
+        <extension>odp</extension>
+        <mime-type>application/vnd.oasis.opendocument.presentation</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Spreadsheet -->
+        <extension>ods</extension>
+        <mime-type>application/vnd.oasis.opendocument.spreadsheet</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Text -->
+        <extension>odt</extension>
+        <mime-type>application/vnd.oasis.opendocument.text</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ogg</extension>
+        <mime-type>application/ogg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Drawing Template -->
+        <extension>otg </extension>
+        <mime-type>application/vnd.oasis.opendocument.graphics-template</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- HTML Document Template -->
+        <extension>oth</extension>
+        <mime-type>application/vnd.oasis.opendocument.text-web</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Presentation Template -->
+        <extension>otp</extension>
+        <mime-type>application/vnd.oasis.opendocument.presentation-template</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Spreadsheet Template -->
+        <extension>ots</extension>
+        <mime-type>application/vnd.oasis.opendocument.spreadsheet-template </mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Text Template -->
+        <extension>ott</extension>
+        <mime-type>application/vnd.oasis.opendocument.text-template</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pbm</extension>
+        <mime-type>image/x-portable-bitmap</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pct</extension>
+        <mime-type>image/pict</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pdf</extension>
+        <mime-type>application/pdf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pgm</extension>
+        <mime-type>image/x-portable-graymap</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pic</extension>
+        <mime-type>image/pict</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pict</extension>
+        <mime-type>image/pict</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pls</extension>
+        <mime-type>audio/x-scpls</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>png</extension>
+        <mime-type>image/png</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pnm</extension>
+        <mime-type>image/x-portable-anymap</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>pnt</extension>
+        <mime-type>image/x-macpaint</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ppm</extension>
+        <mime-type>image/x-portable-pixmap</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ppt</extension>
+        <mime-type>application/powerpoint</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ps</extension>
+        <mime-type>application/postscript</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>psd</extension>
+        <mime-type>image/x-photoshop</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>qt</extension>
+        <mime-type>video/quicktime</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>qti</extension>
+        <mime-type>image/x-quicktime</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>qtif</extension>
+        <mime-type>image/x-quicktime</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ras</extension>
+        <mime-type>image/x-cmu-raster</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>rdf</extension>
+        <mime-type>application/rdf+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>rgb</extension>
+        <mime-type>image/x-rgb</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>rm</extension>
+        <mime-type>application/vnd.rn-realmedia</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>roff</extension>
+        <mime-type>application/x-troff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>rtf</extension>
+        <mime-type>application/rtf</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>rtx</extension>
+        <mime-type>text/richtext</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>sh</extension>
+        <mime-type>application/x-sh</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>shar</extension>
+        <mime-type>application/x-shar</mime-type>
+    </mime-mapping>
+<!--
+    <mime-mapping>
+        <extension>shtml</extension>
+        <mime-type>text/x-server-parsed-html</mime-type>
+    </mime-mapping>
+-->
+    <mime-mapping>
+        <extension>smf</extension>
+        <mime-type>audio/x-midi</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>sit</extension>
+        <mime-type>application/x-stuffit</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>snd</extension>
+        <mime-type>audio/basic</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>src</extension>
+        <mime-type>application/x-wais-source</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>sv4cpio</extension>
+        <mime-type>application/x-sv4cpio</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>sv4crc</extension>
+        <mime-type>application/x-sv4crc</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>swf</extension>
+        <mime-type>application/x-shockwave-flash</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>t</extension>
+        <mime-type>application/x-troff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tar</extension>
+        <mime-type>application/x-tar</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tcl</extension>
+        <mime-type>application/x-tcl</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tex</extension>
+        <mime-type>application/x-tex</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>texi</extension>
+        <mime-type>application/x-texinfo</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>texinfo</extension>
+        <mime-type>application/x-texinfo</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tif</extension>
+        <mime-type>image/tiff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tiff</extension>
+        <mime-type>image/tiff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tr</extension>
+        <mime-type>application/x-troff</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>tsv</extension>
+        <mime-type>text/tab-separated-values</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>txt</extension>
+        <mime-type>text/plain</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ulw</extension>
+        <mime-type>audio/basic</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ustar</extension>
+        <mime-type>application/x-ustar</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>vxml</extension>
+        <mime-type>application/voicexml+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xbm</extension>
+        <mime-type>image/x-xbitmap</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xht</extension>
+        <mime-type>application/xhtml+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xhtml</extension>
+        <mime-type>application/xhtml+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xml</extension>
+        <mime-type>application/xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xpm</extension>
+        <mime-type>image/x-xpixmap</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xsl</extension>
+        <mime-type>application/xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xslt</extension>
+        <mime-type>application/xslt+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xul</extension>
+        <mime-type>application/vnd.mozilla.xul+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xwd</extension>
+        <mime-type>image/x-xwindowdump</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>wav</extension>
+        <mime-type>audio/x-wav</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>svg</extension>
+        <mime-type>image/svg+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>svgz</extension>
+        <mime-type>image/svg+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>vsd</extension>
+        <mime-type>application/x-visio</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- Wireless Bitmap -->
+        <extension>wbmp</extension>
+        <mime-type>image/vnd.wap.wbmp</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- WML Source -->
+        <extension>wml</extension>
+        <mime-type>text/vnd.wap.wml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- Compiled WML -->
+        <extension>wmlc</extension>
+        <mime-type>application/vnd.wap.wmlc</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- WML Script Source -->
+        <extension>wmls</extension>
+        <mime-type>text/vnd.wap.wmlscript</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- Compiled WML Script -->
+        <extension>wmlscriptc</extension>
+        <mime-type>application/vnd.wap.wmlscriptc</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>wmv</extension>
+        <mime-type>video/x-ms-wmv</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>wrl</extension>
+        <mime-type>x-world/x-vrml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>wspolicy</extension>
+        <mime-type>application/wspolicy+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>Z</extension>
+        <mime-type>application/x-compress</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>z</extension>
+        <mime-type>application/x-compress</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>zip</extension>
+        <mime-type>application/zip</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>xls</extension>
+        <mime-type>application/vnd.ms-excel</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>doc</extension>
+        <mime-type>application/vnd.ms-word</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ppt</extension>
+        <mime-type>application/vnd.ms-powerpoint</mime-type>
+    </mime-mapping>
+
+  <!-- ==================== Default Welcome File List ===================== -->
+  <!-- When a request URI refers to a directory, the default servlet looks  -->
+  <!-- for a "welcome file" within that directory and, if present,          -->
+  <!-- to the corresponding resource URI for display.  If no welcome file   -->
+  <!-- is present, the default servlet either serves a directory listing,   -->
+  <!-- or returns a 404 status, depending on how it is configured.          -->
+  <!--                                                                      -->
+  <!-- If you define welcome files in your own application's web.xml        -->
+  <!-- deployment descriptor, that list *replaces* the list configured      -->
+  <!-- here, so be sure that you include any of the default values that     -->
+  <!-- you wish to include.                                                 -->
+
+    <welcome-file-list>
+        <welcome-file>index.html</welcome-file>
+        <welcome-file>index.htm</welcome-file>
+        <welcome-file>index.jsp</welcome-file>
+    </welcome-file-list>
+
+</web-app>
diff --git a/trunk/server/modules/org.argeo.server.catalina/pom.xml b/trunk/server/modules/org.argeo.server.catalina/pom.xml
new file mode 100644 (file)
index 0000000..48a7da3
--- /dev/null
@@ -0,0 +1,43 @@
+<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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.catalina</artifactId>
+       <name>Commons Server Default Catalina Service (Tomcat)</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Fragment-Host>org.apache.catalina</Fragment-Host>
+                                               <!-- Hack for HTTP session deserialization in OSGi -->
+                                               <Import-Package>
+                                                       *,
+                                                       org.apache.commons.logging.impl;resolution:=optional,
+                                                       org.argeo.security.core;resolution:=optional,
+                                                       org.argeo.security.jcr;resolution:=optional,
+                                                       org.springframework.beans.factory.support;resolution:=optional,
+                                                       org.springframework.context.annotation;resolution:=optional,
+                                                       org.springframework.orm.jpa.support;resolution:=optional,
+                                                       org.springframework.security;resolution:=optional,
+                                                       org.springframework.security.context;resolution:=optional,
+                                                       org.springframework.security.providers;resolution:=optional,
+                                                       org.springframework.security.providers.rememberme;resolution:=optional,
+                                                       org.springframework.security.ui;resolution:=optional,
+                                                       org.springframework.security.ui.savedrequest;resolution:=optional,
+                                                       org.springframework.security.userdetails;resolution:=optional,
+                                                       org.springframework.security.providers.preauth;resolution:=optional,
+                                                       org.springframework.web.context.request;resolution:=optional
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.jdbc/.project b/trunk/server/modules/org.argeo.server.jdbc/.project
new file mode 100644 (file)
index 0000000..283257b
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.jdbc</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/server/modules/org.argeo.server.jdbc/.settings/org.eclipse.pde.core.prefs b/trunk/server/modules/org.argeo.server.jdbc/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..e9fc089
--- /dev/null
@@ -0,0 +1,4 @@
+#Tue Jun 15 17:44:26 CEST 2010
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/trunk/server/modules/org.argeo.server.jdbc/build.properties b/trunk/server/modules/org.argeo.server.jdbc/build.properties
new file mode 100644 (file)
index 0000000..5f22cdd
--- /dev/null
@@ -0,0 +1 @@
+bin.includes = META-INF/
diff --git a/trunk/server/modules/org.argeo.server.jdbc/pom.xml b/trunk/server/modules/org.argeo.server.jdbc/pom.xml
new file mode 100644 (file)
index 0000000..e1a8905
--- /dev/null
@@ -0,0 +1,36 @@
+<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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.jdbc</artifactId>
+       <name>Commons Server Spring JDBC Drivers for OSGi</name>
+       <description>Makes commons JDBC drivers visible to Spring JDBC in an OSGi runtime</description>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Fragment-Host>org.springframework.jdbc</Fragment-Host>
+                                               <Import-Package>
+                                                       *,
+                                                       com.microsoft.sqlserver.jdbc;resolution:=optional,
+                                                       com.mysql.jdbc;resolution:=optional,
+                                                       net.sourceforge.jtds.jdbc;resolution:=optional,
+                                                       oracle.jdbc;resolution:=optional,
+                                                       org.apache.derby.jdbc;resolution:=optional,
+                                                       org.h2;resolution:=optional,
+                                                       org.hsqldb;resolution:=optional,
+                                                       org.postgresql;resolution:=optional
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.rap.webapp/.project b/trunk/server/modules/org.argeo.server.rap.webapp/.project
new file mode 100644 (file)
index 0000000..3ffaae1
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.rap.webapp</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <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>
+       </natures>
+</projectDescription>
diff --git a/trunk/server/modules/org.argeo.server.rap.webapp/.settings/org.eclipse.pde.core.prefs b/trunk/server/modules/org.argeo.server.rap.webapp/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..fd884c2
--- /dev/null
@@ -0,0 +1,4 @@
+#Tue Mar 01 15:09:47 CET 2011
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/trunk/server/modules/org.argeo.server.rap.webapp/META-INF/context-template.xml b/trunk/server/modules/org.argeo.server.rap.webapp/META-INF/context-template.xml
new file mode 100644 (file)
index 0000000..eaf6e86
--- /dev/null
@@ -0,0 +1,4 @@
+<!-- Spring OSGi Web 1.2.1 doesn't take context.xml files into account, so 
+       we set cookies to false at deployment -->
+<Context cookies="false">
+</Context>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/applicationContext.xml b/trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/applicationContext.xml
new file mode 100644 (file)
index 0000000..3368624
--- /dev/null
@@ -0,0 +1,20 @@
+<?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:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
+
+       <import resource="osgi.xml" />
+       <import resource="security-filters.xml" />
+       <!-- <import resource="security.xml" /> -->
+
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:rap-webapp.properties</value>
+               </property>
+       </bean>
+
+</beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/osgi.xml b/trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/osgi.xml
new file mode 100644 (file)
index 0000000..fec4433
--- /dev/null
@@ -0,0 +1,16 @@
+<?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:security="http://www.springframework.org/schema/security"\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/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">\r
+\r
+       <reference id="authenticationManager"\r
+               interface="org.springframework.security.AuthenticationManager" />\r
+       <reference id="userDetailsService"\r
+               interface="org.springframework.security.userdetails.UserDetailsService"\r
+               cardinality="0..1" />\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/security-filters.xml b/trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/security-filters.xml
new file mode 100644 (file)
index 0000000..4c7df6b
--- /dev/null
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:sec="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
+
+       <bean id="springSecurityFilterChain" class="org.springframework.security.util.FilterChainProxy">
+               <sec:filter-chain-map path-type="ant">
+                       <sec:filter-chain pattern="/private"
+                               filters="session,x509,basic,rememberMe,exception,interceptor" />
+                       <sec:filter-chain pattern="/basicauth"
+                               filters="session,x509,basic,exception,interceptor" />
+                       <sec:filter-chain pattern="/clientauth"
+                               filters="session,x509,exception,interceptor" />
+                       <!-- <sec:filter-chain pattern="/node" filters="session,x509,exception,interceptor" /> -->
+                       <sec:filter-chain pattern="/public"
+                               filters="session,anonymous,exception,interceptorPublic" />
+                       <sec:filter-chain pattern="/j_spring_security_logout"
+                               filters="session,logout,exception" />
+               </sec:filter-chain-map>
+       </bean>
+
+       <!-- The actual authorization checks (called last, but first here for ease 
+               of configuration) -->
+       <bean id="interceptor" parent="filterInvocationInterceptorTemplate">
+               <property name="objectDefinitionSource">
+                       <value>
+                               PATTERN_TYPE_APACHE_ANT
+                               /**=ROLE_USER,ROLE_ADMIN
+                       </value>
+               </property>
+       </bean>
+       <bean id="interceptorPublic" parent="filterInvocationInterceptorTemplate">
+               <property name="objectDefinitionSource">
+                       <value>
+                               PATTERN_TYPE_APACHE_ANT
+                               /**=IS_AUTHENTICATED_ANONYMOUSLY
+                       </value>
+               </property>
+       </bean>
+
+       <bean id="x509"
+               class="org.springframework.security.ui.preauth.x509.X509PreAuthenticatedProcessingFilter">
+               <property name="authenticationManager" ref="authenticationManager" />
+               <property name="principalExtractor">
+                       <bean
+                               class="org.springframework.security.ui.preauth.x509.SubjectDnX509PrincipalExtractor">
+                               <property name="subjectDnRegex" value="CN=(.*?)," />
+                       </bean>
+               </property>
+       </bean>
+
+       <!-- Integrates the authentication information in the http sessions -->
+       <bean id="session"
+               class="org.springframework.security.context.HttpSessionContextIntegrationFilter">
+               <property name="allowSessionCreation" value="true" />
+       </bean>
+
+       <!-- Processes logouts, removing both session informations and the remember-me 
+               cookie from the browser -->
+       <bean id="logout" class="org.springframework.security.ui.logout.LogoutFilter">
+               <constructor-arg value="/logout" />
+               <constructor-arg>
+                       <list>
+                               <ref bean="rememberMeServices" />
+                               <bean
+                                       class="org.springframework.security.ui.logout.SecurityContextLogoutHandler" />
+                       </list>
+               </constructor-arg>
+       </bean>
+
+       <!-- Use the remember me cookie to authenticate -->
+       <bean id="rememberMe"
+               class="org.springframework.security.ui.rememberme.RememberMeProcessingFilter">
+               <property name="authenticationManager" ref="authenticationManager" />
+               <property name="rememberMeServices" ref="rememberMeServices" />
+       </bean>
+
+       <bean id="rememberMeServices"
+               class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices">
+               <property name="userDetailsService" ref="userDetailsService" />
+               <property name="key" value="${argeo.security.systemKey}" />
+               <property name="alwaysRemember" value="true" />
+       </bean>
+
+       <!-- Basic authentication -->
+       <bean id="basic"
+               class="org.springframework.security.ui.basicauth.BasicProcessingFilter">
+               <property name="authenticationManager">
+                       <ref bean="authenticationManager" />
+               </property>
+               <property name="authenticationEntryPoint">
+                       <ref local="basicProcessingFilterEntryPoint" />
+               </property>
+               <property name="rememberMeServices" ref="rememberMeServices" />
+       </bean>
+
+       <!-- Activate basic auth when needed -->
+       <bean id="basicProcessingFilterEntryPoint"
+               class="org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint">
+               <property name="realmName">
+                       <value>${argeo.server.realmName}</value>
+               </property>
+       </bean>
+
+       <!-- If everything else failed, anonymous authentication -->
+       <bean id="anonymous"
+               class="org.springframework.security.providers.anonymous.AnonymousProcessingFilter">
+               <property name="key" value="${argeo.security.systemKey}" />
+               <property name="userAttribute" value="anonymous,ROLE_ANONYMOUS" />
+       </bean>
+
+       <!-- Reacts to security related exceptions -->
+       <bean id="exception"
+               class="org.springframework.security.ui.ExceptionTranslationFilter">
+               <property name="authenticationEntryPoint">
+                       <ref bean="basicProcessingFilterEntryPoint" />
+               </property>
+               <property name="accessDeniedHandler">
+                       <bean class="org.springframework.security.ui.AccessDeniedHandlerImpl">
+                               <property name="errorPage" value="/error" />
+                       </bean>
+               </property>
+       </bean>
+
+       <!-- Template for authorization checks -->
+       <bean id="filterInvocationInterceptorTemplate" abstract="true"
+               class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
+               <property name="authenticationManager" ref="authenticationManager" />
+               <property name="accessDecisionManager">
+                       <bean class="org.springframework.security.vote.AffirmativeBased">
+                               <property name="allowIfAllAbstainDecisions" value="false" />
+                               <property name="decisionVoters">
+                                       <list>
+                                               <bean class="org.springframework.security.vote.RoleVoter" />
+                                               <bean class="org.springframework.security.vote.AuthenticatedVoter" />
+                                       </list>
+                               </property>
+                       </bean>
+               </property>
+       </bean>
+</beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/service-servlet.xml b/trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/service-servlet.xml
new file mode 100644 (file)
index 0000000..7a05219
--- /dev/null
@@ -0,0 +1,28 @@
+<?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:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
+       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
+
+
+       <bean id="urlMapping"
+               class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
+               <property name="mappings">
+                       <props>
+                               <prop key="*">osgiServiceController</prop>
+                       </props>
+               </property>
+       </bean>
+
+       <bean id="osgiServiceController"
+               class="org.springframework.web.servlet.mvc.ServletWrappingController">
+               <property name="servletClass">
+                       <value>org.eclipse.equinox.http.servlet.HttpServiceServlet</value>
+               </property>
+               <property name="servletName">
+                       <value>osgiService</value>
+               </property>
+       </bean>
+</beans>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/web.xml b/trunk/server/modules/org.argeo.server.rap.webapp/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..1b6e766
--- /dev/null
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+       version="2.5">
+
+       <display-name>Argeo OSGi Webapp</display-name>
+
+       <!-- General -->
+       <context-param>
+               <param-name>contextConfigLocation</param-name>
+               <param-value>/WEB-INF/applicationContext.xml</param-value>
+       </context-param>
+
+       <listener>
+               <display-name>Spring Context</display-name>
+               <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+       </listener>
+       <context-param>
+               <param-name>contextClass</param-name>
+               <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
+       </context-param>
+
+       <!-- OSGi Http Service -->
+       <servlet>
+               <servlet-name>service</servlet-name>
+               <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+               <init-param>
+                       <param-name>contextClass</param-name>
+                       <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
+               </init-param>
+               <load-on-startup>1</load-on-startup>
+       </servlet>
+
+       <servlet-mapping>
+               <servlet-name>service</servlet-name>
+               <url-pattern>/*</url-pattern>
+       </servlet-mapping>
+
+       <!-- Security -->
+       <filter>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
+       </filter>
+<!---->
+       <filter-mapping>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <url-pattern>/private</url-pattern>
+       </filter-mapping>
+       <filter-mapping>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <url-pattern>/basicauth</url-pattern>
+       </filter-mapping>
+       <filter-mapping>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <url-pattern>/clientauth</url-pattern>
+       </filter-mapping>
+       <filter-mapping>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <url-pattern>/none</url-pattern>
+       </filter-mapping>
+       <filter-mapping>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <url-pattern>/public</url-pattern>
+       </filter-mapping>
+
+</web-app>
diff --git a/trunk/server/modules/org.argeo.server.rap.webapp/build.properties b/trunk/server/modules/org.argeo.server.rap.webapp/build.properties
new file mode 100644 (file)
index 0000000..29dea25
--- /dev/null
@@ -0,0 +1,2 @@
+bin.includes = META-INF/,\
+               WEB-INF/
diff --git a/trunk/server/modules/org.argeo.server.rap.webapp/pom.xml b/trunk/server/modules/org.argeo.server.rap.webapp/pom.xml
new file mode 100644 (file)
index 0000000..4891a28
--- /dev/null
@@ -0,0 +1,54 @@
+<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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>modules</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.rap.webapp</artifactId>
+       <name>Commons Server RAP Webapp</name>
+       <description>Integrates into OSGi HTTP registry</description>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Web-ContextPath>ui</Web-ContextPath>
+                                               <Import-Package>
+                                                       *,
+
+                                                       org.springframework.beans.factory.config,
+                                                       
+                                                       org.eclipse.equinox.http.servlet,
+                                                       org.springframework.osgi.web.context.support,
+                                                       org.springframework.security,
+                                                       org.springframework.security.context,
+                                                       org.springframework.security.intercept.web,
+                                                       org.springframework.security.providers.anonymous,
+                                                       org.springframework.security.ui,
+                                                       org.springframework.security.ui.basicauth,
+                                                       org.springframework.security.ui.logout,
+                                                       org.springframework.security.ui.rememberme,
+                                                       org.springframework.security.ui.webapp,
+                                                       org.springframework.security.ui.preauth.x509,
+                                                       org.springframework.security.userdetails,
+                                                       org.springframework.security.util,
+                                                       org.springframework.security.vote,
+                                                       org.springframework.security.wrapper,
+                                                       org.springframework.web.context,
+                                                       org.springframework.web.filter,
+                                                       org.springframework.web.servlet,
+                                                       org.springframework.web.servlet.handler,
+                                                       org.springframework.web.servlet.mvc,
+                                                       
+                                                       
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/server/modules/org.argeo.server.rap.webapp/rap-webapp.properties b/trunk/server/modules/org.argeo.server.rap.webapp/rap-webapp.properties
new file mode 100644 (file)
index 0000000..012255a
--- /dev/null
@@ -0,0 +1,2 @@
+argeo.security.systemKey=argeo
+argeo.server.realmName=Argeo
\ No newline at end of file
diff --git a/trunk/server/modules/pom.xml b/trunk/server/modules/pom.xml
new file mode 100644 (file)
index 0000000..28b7661
--- /dev/null
@@ -0,0 +1,42 @@
+<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.11</version>
+               <artifactId>server</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <groupId>org.argeo.commons.server</groupId>
+       <artifactId>modules</artifactId>
+       <packaging>pom</packaging>
+       <name>Commons Server Modules</name>
+       <modules>
+               <module>org.argeo.ext.jackrabbit.sybase</module>
+               <module>org.argeo.ext.jdbm</module>
+               <module>org.argeo.ext.bsf</module>
+               <module>org.argeo.jackrabbit.webapp</module>
+               <module>org.argeo.node.repo.jackrabbit</module>
+               <module>org.argeo.server.activemq.broker</module>
+               <module>org.argeo.server.ads.server</module>
+               <module>org.argeo.server.catalina</module>
+               <module>org.argeo.server.jdbc</module>
+               <module>org.argeo.server.rap.webapp</module>
+<!--           <module>org.argeo.server.tomcat</module> -->
+       </modules>
+       <build>
+               <resources>
+                       <resource>
+                               <directory>.</directory>
+                               <includes>
+                                       <include>**</include>
+                               </includes>
+                               <excludes>
+                                       <exclude>.*</exclude>
+                                       <exclude>.*/**</exclude>
+                                       <exclude>pom.xml</exclude>
+                                       <exclude>build.properties</exclude>
+                               </excludes>
+                       </resource>
+               </resources>
+       </build>
+</project>
\ No newline at end of file
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/.classpath b/trunk/server/plugins/org.argeo.jcr.ui.explorer/.classpath
new file mode 100644 (file)
index 0000000..527d8ab
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="src" path="src/main/resources"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/.project b/trunk/server/plugins/org.argeo.jcr.ui.explorer/.project
new file mode 100644 (file)
index 0000000..162cd6b
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.jcr.ui.explorer</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/trunk/server/plugins/org.argeo.jcr.ui.explorer/.settings/org.eclipse.jdt.core.prefs b/trunk/server/plugins/org.argeo.jcr.ui.explorer/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..992c5d7
--- /dev/null
@@ -0,0 +1,8 @@
+#Thu Feb 24 08:49:06 CET 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/commands.xml b/trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/commands.xml
new file mode 100644 (file)
index 0000000..ef9f3f1
--- /dev/null
@@ -0,0 +1,50 @@
+<?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="openGenericJcrQueryEditor"
+               class="org.argeo.eclipse.ui.jcr.commands.OpenGenericJcrQueryEditor"
+               scope="prototype">
+               <property name="editorId"
+                       value="org.argeo.jcr.ui.explorer.genericJcrQueryEditor" />
+       </bean>
+
+       <bean id="openGenericNodeEditor"
+               class="org.argeo.jcr.ui.explorer.commands.OpenGenericNodeEditor"
+               scope="prototype">
+       </bean>
+
+       <bean id="getNodeSize" class="org.argeo.jcr.ui.explorer.commands.GetNodeSize"
+               scope="prototype">
+       </bean>
+
+       <bean id="addRemoteRepository" class="org.argeo.jcr.ui.explorer.commands.AddRemoteRepository">
+               <property name="repositoryFactory" ref="repositoryFactory" />
+               <property name="nodeRepository" ref="nodeRepository" />
+               <property name="keyring" ref="keyring" />
+       </bean>
+
+       <bean id="removeRemoteRepository"
+               class="org.argeo.jcr.ui.explorer.commands.RemoveRemoteRepository">
+       </bean>
+
+       <bean id="addFolderNode" class="org.argeo.jcr.ui.explorer.commands.AddFolderNode"
+               scope="prototype" />
+
+       <bean id="addPrivileges" class="org.argeo.jcr.ui.explorer.commands.AddPrivileges"
+               scope="prototype" />
+
+       <bean id="createWorkspace" class="org.argeo.jcr.ui.explorer.commands.CreateWorkspace"
+               scope="prototype" />
+
+       <bean id="refresh" class="org.argeo.jcr.ui.explorer.commands.Refresh"
+               scope="prototype" />
+
+       <bean id="deleteNodes" class="org.argeo.jcr.ui.explorer.commands.DeleteNodes"
+               scope="prototype" />
+
+       <bean id="importFileSystem" class="org.argeo.jcr.ui.explorer.commands.ImportFileSystem"
+               scope="prototype" />
+</beans>
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/editors.xml b/trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/editors.xml
new file mode 100644 (file)
index 0000000..fc86cee
--- /dev/null
@@ -0,0 +1,15 @@
+<?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:util="http://www.springframework.org/schema/util"
+       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
+        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
+
+       <bean id="genericJcrQueryEditor" class="org.argeo.jcr.ui.explorer.editors.GenericJcrQueryEditor"
+               scope="prototype">
+               <property name="session" ref="nodeSession" />
+       </bean>
+       <bean id="genericNodeEditor" class="org.argeo.jcr.ui.explorer.editors.GenericNodeEditor"
+               scope="prototype">
+       </bean>
+</beans>
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/jcr.xml b/trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/jcr.xml
new file mode 100644 (file)
index 0000000..e074154
--- /dev/null
@@ -0,0 +1,10 @@
+<?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:util="http://www.springframework.org/schema/util"
+       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
+        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
+
+       <bean id="repositoryRegister" class="org.argeo.jcr.DefaultRepositoryRegister" />
+
+</beans>
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/osgi.xml b/trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/osgi.xml
new file mode 100644 (file)
index 0000000..255462b
--- /dev/null
@@ -0,0 +1,23 @@
+<?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
+       <list id="repositories" interface="javax.jcr.Repository"\r
+               cardinality="0..N">\r
+               <listener ref="repositoryRegister" bind-method="register"\r
+                       unbind-method="unregister" />\r
+       </list>\r
+\r
+       <reference id="nodeRepository" interface="javax.jcr.Repository"\r
+               filter="(argeo.jcr.repository.alias=node)" />\r
+       <reference id="repositoryFactory" interface="javax.jcr.RepositoryFactory" />\r
+\r
+       <reference id="keyring" interface="org.argeo.security.crypto.CryptoKeyring" />\r
+\r
+</beans:beans>
\ No newline at end of file
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/views.xml b/trunk/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/views.xml
new file mode 100644 (file)
index 0000000..14f1943
--- /dev/null
@@ -0,0 +1,15 @@
+<?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">
+
+       <!-- Views -->
+       <bean id="browserView" class="org.argeo.jcr.ui.explorer.views.GenericJcrBrowser"
+               scope="prototype">
+               <property name="repositoryRegister" ref="repositoryRegister" />
+               <property name="repositoryFactory" ref="repositoryFactory" />
+               <property name="nodeRepository" ref="nodeRepository" />
+               <property name="keyring" ref="keyring" />
+       </bean>
+</beans>
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/build.properties b/trunk/server/plugins/org.argeo.jcr.ui.explorer/build.properties
new file mode 100644 (file)
index 0000000..9b0c715
--- /dev/null
@@ -0,0 +1,7 @@
+source.. =     src/main/java/,\
+                       src/main/resources
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml,\
+               plugin.properties
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/add.gif b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/add.gif
new file mode 100644 (file)
index 0000000..252d7eb
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/add.gif differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addFolder.gif b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addFolder.gif
new file mode 100755 (executable)
index 0000000..d3f43d9
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addFolder.gif differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addPrivileges.png b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addPrivileges.png
new file mode 100644 (file)
index 0000000..a6b251f
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addPrivileges.png differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addRepo.gif b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addRepo.gif
new file mode 100755 (executable)
index 0000000..26d81c0
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addRepo.gif differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addWorkspace.png b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addWorkspace.png
new file mode 100644 (file)
index 0000000..bbee775
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/addWorkspace.png differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/browser.gif b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/browser.gif
new file mode 100644 (file)
index 0000000..6c7320c
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/browser.gif differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/dumpNode.gif b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/dumpNode.gif
new file mode 100644 (file)
index 0000000..14eb1be
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/dumpNode.gif differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/getSize.gif b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/getSize.gif
new file mode 100755 (executable)
index 0000000..b05bf3e
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/getSize.gif differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/import_fs.png b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/import_fs.png
new file mode 100644 (file)
index 0000000..d7c890c
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/import_fs.png differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/nodes.gif b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/nodes.gif
new file mode 100644 (file)
index 0000000..bba3dbc
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/nodes.gif differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/query.png b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/query.png
new file mode 100644 (file)
index 0000000..54c089d
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/query.png differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/refresh.png b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/refresh.png
new file mode 100644 (file)
index 0000000..a3884fb
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/refresh.png differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/remove.gif b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/remove.gif
new file mode 100644 (file)
index 0000000..0ae6dec
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/remove.gif differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/repositories.gif b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/repositories.gif
new file mode 100644 (file)
index 0000000..c13bea1
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/repositories.gif differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/sort.gif b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/sort.gif
new file mode 100644 (file)
index 0000000..23c5d0b
Binary files /dev/null and b/trunk/server/plugins/org.argeo.jcr.ui.explorer/icons/sort.gif differ
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/plugin.properties b/trunk/server/plugins/org.argeo.jcr.ui.explorer/plugin.properties
new file mode 100644 (file)
index 0000000..6dff863
--- /dev/null
@@ -0,0 +1,2 @@
+## commands label
+addNewFolderCmdLbl=Add folder
\ No newline at end of file
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/plugin.xml b/trunk/server/plugins/org.argeo.jcr.ui.explorer/plugin.xml
new file mode 100644 (file)
index 0000000..67b8751
--- /dev/null
@@ -0,0 +1,360 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+       <!-- Perspectives -->
+   <extension
+         point="org.eclipse.ui.perspectives">
+      <perspective
+            class="org.argeo.jcr.ui.explorer.JcrExplorerPerspective"
+            icon="icons/nodes.gif"
+            id="org.argeo.jcr.ui.explorer.perspective"
+            name="Data Explorer">
+      </perspective>
+   </extension>
+   <!-- Views --> 
+   <extension
+         point="org.eclipse.ui.views">
+          <view
+          class="org.argeo.eclipse.spring.SpringExtensionFactory"
+          icon="icons/browser.gif"
+          id="org.argeo.jcr.ui.explorer.browserView"
+          name="JCR Browser">
+          </view>
+   </extension>
+   <!-- Editors --> 
+   <extension
+           point="org.eclipse.ui.editors">
+            <editor
+                 class="org.argeo.eclipse.spring.SpringExtensionFactory"
+              id="org.argeo.jcr.ui.explorer.genericJcrQueryEditor"
+              name="JCR Query"
+              icon="icons/query.png"
+              default="false">
+        </editor>
+            <editor
+                 class="org.argeo.eclipse.spring.SpringExtensionFactory"
+              id="org.argeo.jcr.ui.explorer.genericNodeEditor"
+              name="Node Editor"
+              icon="icons/query.png"
+              default="false">
+        </editor>
+     </extension>
+       <!-- Commands --> 
+       <extension
+         point="org.eclipse.ui.commands">
+               <command
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            id="org.argeo.jcr.ui.explorer.openGenericJcrQueryEditor"
+            name="New generic JCR query">
+       </command>
+       <command
+                       defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+                       id="org.argeo.jcr.ui.explorer.openGenericNodeEditor"
+                       name="Open generic node Editor">
+                       <commandParameter
+                               id="org.argeo.jcr.ui.explorer.nodePath"
+                               name="Node path">
+                       </commandParameter>
+               </command>    
+       <command
+                       defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+                       id="org.argeo.jcr.ui.explorer.getNodeSize"
+                       name="Get node size">
+               </command>    
+       <command
+                       defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+                       id="org.argeo.jcr.ui.explorer.addRemoteRepository"
+                       name="Add remote JCR repository">
+                       <commandParameter
+                               id="org.argeo.jcr.ui.explorer.repositoryUri"
+                               name="Repository URI">
+                       </commandParameter>
+               </command>    
+       <command
+                       defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+                       id="org.argeo.jcr.ui.explorer.removeRemoteRepository"
+                       name="Remove remote JCR repository">
+               </command>    
+         <command
+               defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+               id="org.argeo.jcr.ui.explorer.addFolderNode"
+               name="Create a new folder">
+         </command>
+         <command
+               defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+               id="org.argeo.jcr.ui.explorer.addPrivileges"
+               name="Add Privileges">
+         </command>
+         <command
+               defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+               id="org.argeo.jcr.ui.explorer.createWorkspace"
+               name="Create a new workspace">
+         </command>
+         <command
+               defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+               id="org.argeo.jcr.ui.explorer.refresh"
+               name="Refresh">
+         </command>
+         <command
+               defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+               id="org.argeo.jcr.ui.explorer.deleteNodes"
+               name="Delete nodes">
+         </command>
+         <command
+               defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+               id="org.argeo.jcr.ui.explorer.importFileSystem"
+               name="Import files...">
+         </command>
+        <!-- <command
+               defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+               id="org.argeo.jcr.ui.explorer.openFile"
+               name="Open current file">
+         </command>
+         -->
+         <command
+               defaultHandler="org.argeo.jcr.ui.explorer.commands.DumpNode"
+               id="org.argeo.jcr.ui.explorer.dumpNode"
+               name="Dump Current Selected Node">
+         </command>
+         <command
+            defaultHandler="org.argeo.jcr.ui.explorer.commands.SortChildNodes"
+            id="org.argeo.jcr.ui.explorer.sortChildNodes"
+            name="Sort node tree">
+            <!-- FIXME: default value does not work -->
+            <state 
+                               id="org.argeo.jcr.ui.explorer.sortChildNodes.toggleState" 
+                               class="org.eclipse.ui.handlers.RegistryToggleState:true" >
+                               <!-- <class class="org.eclipse.jface.commands.ToggleState"> 
+                                       <parameter
+                                               name="default"
+                                       value="true" />
+                               </class> -->
+                       </state>
+     </command>
+    </extension>
+
+    <!-- Menus --> 
+       <extension point="org.eclipse.ui.menus">
+               <!-- Browser view specific menu --> 
+               <menuContribution
+                       locationURI="menu:org.argeo.jcr.ui.explorer.browserView">
+            <!-- See bug 149 --> 
+            <!-- <command
+               commandId="org.argeo.jcr.ui.explorer.openGenericJcrQueryEditor"
+                icon="icons/query.png"
+                style="push">
+            </command> --> 
+            <command
+               commandId="org.argeo.jcr.ui.explorer.addRemoteRepository"
+                icon="icons/addRepo.gif"
+                style="push">
+            </command>
+             <command
+               commandId="org.argeo.jcr.ui.explorer.sortChildNodes"
+                icon="icons/sort.gif"
+                style="toggle"
+                label="Sort child nodes"
+                tooltip="Warning: stopping to sort children nodes might enhance overall performances">
+            </command>
+               </menuContribution>
+               <!-- Browser view popup context menu --> 
+               <menuContribution
+                       locationURI="popup:org.argeo.jcr.ui.explorer.browserView">
+                       <command
+                               commandId="org.argeo.jcr.ui.explorer.refresh"
+                               icon="icons/refresh.png"
+                               style="push">
+                       </command>
+                       <command
+                        commandId="org.argeo.jcr.ui.explorer.addFolderNode"
+                        icon="icons/addFolder.gif"
+                        label="Add Folder"
+                        style="push">
+                               <visibleWhen>
+                                       <iterate>
+                                     <and>
+                                        <or>
+                                           <instanceof
+                                                 value="org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem">
+                                           </instanceof>
+                                           <instanceof
+                                                 value="org.argeo.jcr.ui.explorer.model.WorkspaceElem">
+                                           </instanceof>
+                                        </or>
+                               <with variable="activeMenuSelection"><count value="1"/></with>
+                                     </and>
+                                       </iterate>
+                               </visibleWhen>
+                       </command>
+                       <command
+                        commandId="org.argeo.jcr.ui.explorer.addPrivileges"
+                        icon="icons/addPrivileges.png"
+                        label="Add Privileges"
+                        style="push">
+                               <visibleWhen>
+                                       <iterate>
+                                     <and>
+                                        <or>
+                                           <instanceof
+                                                 value="org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem">
+                                           </instanceof>
+                                           <instanceof
+                                                 value="org.argeo.jcr.ui.explorer.model.WorkspaceElem">
+                                           </instanceof>
+                                        </or>
+                               <with variable="activeMenuSelection"><count value="1"/></with>
+                                     </and>
+                                       </iterate>
+                               </visibleWhen>
+                       </command>
+                       <command
+                        commandId="org.argeo.jcr.ui.explorer.createWorkspace"
+                        icon="icons/addWorkspace.png"
+                        label="Create Workspace"
+                        style="push">
+                               <visibleWhen>
+                                       <iterate>
+                                       <and>
+                                               <or>
+                                               <instanceof
+                                                       value="org.argeo.jcr.ui.explorer.model.RepositoryElem">
+                                               </instanceof>
+                                               </or>
+                                       <with variable="activeMenuSelection"><count value="1"/></with>
+                                               </and>
+                                       </iterate>
+                               </visibleWhen>
+                       </command>
+                       <command
+                               commandId="org.argeo.jcr.ui.explorer.deleteNodes"
+                               icon="icons/remove.gif"
+                               label="Delete Nodes"
+                               style="push">
+                               <visibleWhen>
+                                       <iterate>
+                                               <or>
+                                                       <instanceof
+                                                               value="org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem" />
+                                                       <instanceof
+                                                               value="org.argeo.jcr.ui.explorer.model.WorkspaceElem" />
+                                               </or>
+                                       </iterate>
+                               </visibleWhen>
+                       </command>
+                       <command
+                               commandId="org.argeo.jcr.ui.explorer.importFileSystem"
+                               icon="icons/import_fs.png"
+                               style="push"
+                               tooltip="Import files from the files sytem">
+                               <visibleWhen>
+                                       <iterate>
+                                               <and>
+                                                       <or>
+                                                               <instanceof
+                                                                       value="org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem" />
+                                                               <instanceof
+                                               value="org.argeo.jcr.ui.explorer.model.WorkspaceElem" />
+                                       </or>
+                                       <with variable="activeMenuSelection"><count value="1"/></with>
+                                               </and>
+                                       </iterate>
+                               </visibleWhen>
+                       </command>
+                       <command
+                               commandId="org.argeo.jcr.ui.explorer.addRemoteRepository"
+                               icon="icons/addRepo.gif"
+                               style="push">
+                                       <visibleWhen>
+                                               <iterate> 
+                                                       <or>
+                                                               <instanceof
+                                               value="org.argeo.jcr.ui.explorer.model.RepositoriesElem" />
+                                                               <instanceof
+                                                                       value="org.argeo.jcr.ui.explorer.model.RepositoryElem" />
+                                                       </or> 
+                                               </iterate>
+                                       </visibleWhen>
+                       </command>
+                       <command
+                               commandId="org.argeo.jcr.ui.explorer.removeRemoteRepository"
+                               icon="icons/remove.gif"
+                               style="push">
+                               <visibleWhen>
+                                       <iterate> 
+                                               <or>
+                                                       <instanceof
+                                                               value="org.argeo.jcr.ui.explorer.model.RemoteRepositoryElem" />
+                                               </or> 
+                               </iterate>
+                               </visibleWhen>
+                       </command>
+                       <command
+                               commandId="org.argeo.jcr.ui.explorer.getNodeSize"
+                               icon="icons/getSize.gif"
+                               style="push">
+                                       <visibleWhen>
+                                               <iterate>
+                                                       <or>
+                                                               <instanceof
+                                                                       value="org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem" />
+                                                               <instanceof
+                                                                       value="org.argeo.jcr.ui.explorer.model.WorkspaceElem" />
+                                       </or>
+                                       </iterate>
+                                       </visibleWhen>
+                       </command>
+                       <command
+                        commandId="org.argeo.jcr.ui.explorer.dumpNode"
+                        icon="icons/dumpNode.gif"
+                        label="Dump Node"
+                        style="push">
+                               <visibleWhen>
+                                       <iterate>
+                                     <and>
+                                               <instanceof value="org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem"></instanceof>
+                               <with variable="activeMenuSelection"><count value="1"/></with>
+                                     </and>
+                                       </iterate>
+                               </visibleWhen>
+                       </command>
+                       
+               </menuContribution>
+       </extension>
+
+       <!-- Reduce visibility of JCR Explorer perspective to users that are in ROLE_ADMIN -->  
+       <extension
+       point="org.eclipse.ui.activities">
+               <activity
+                       description="Only for admins"
+                       id="org.argeo.jcr.ui.explorer.adminActivity"
+            name="Jcr Technical Admin">
+                       <enabledWhen>
+                               <with variable="roles">
+                                       <iterate ifEmpty="false" operator="or">
+                                               <equals value="ROLE_ADMIN" />
+                                       </iterate>
+                               </with>
+                       </enabledWhen>
+               </activity>
+        <activityPatternBinding
+                       activityId="org.argeo.jcr.ui.explorer.adminActivity"
+                       isEqualityPattern="true"
+                       pattern="org.argeo.jcr.ui.explorer/org.argeo.jcr.ui.explorer.perspective">
+               </activityPatternBinding>
+       </extension>
+
+       <!-- Core expression definition 
+       <extension
+         point="org.eclipse.core.expressions.definitions">
+      <definition
+            id="expression.onlyOneItemSelected">
+         <with
+               variable="activeMenuSelection">
+            <count
+                  value="1">
+            </count>
+         </with>
+      </definition>
+   </extension>
+       --> 
+</plugin>
\ No newline at end of file
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/pom.xml b/trunk/server/plugins/org.argeo.jcr.ui.explorer/pom.xml
new file mode 100644 (file)
index 0000000..548e616
--- /dev/null
@@ -0,0 +1,61 @@
+<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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>plugins</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.jcr.ui.explorer</artifactId>
+       <name>Commons JCR Explorer</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-ActivationPolicy>lazy</Bundle-ActivationPolicy>
+                                               <Bundle-Localization>plugin</Bundle-Localization>
+                                               <Bundle-Activator>org.argeo.jcr.ui.explorer.JcrExplorerPlugin</Bundle-Activator>
+                                               <Require-Bundle>org.eclipse.ui;resolution:=optional,
+                                                       org.eclipse.core.runtime;resolution:=optional,
+                                                       org.eclipse.rap.ui;resolution:=optional,
+                                                       org.eclipse.rap.ui.workbench;resolution:=optional</Require-Bundle>
+                                               <Import-Package>
+                                                       *,
+                                                       org.argeo.eclipse.spring,
+                                               </Import-Package>
+                                               <Export-Package>org.argeo.jcr.ui.explorer.*</Export-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui.jcr</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- RCP only dependency, needed at compile time -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.dep.rcp</artifactId>
+                       <version>2.1.11</version>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.eclipse.ui.rcp</artifactId>
+                       <version>2.1.11</version>
+                       <scope>provided</scope>
+               </dependency>
+       </dependencies>
+</project>
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerConstants.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerConstants.java
new file mode 100644 (file)
index 0000000..a9cb258
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.ui.explorer;
+
+/** Constants used across the application. */
+public interface JcrExplorerConstants {
+       /*
+        * MISCEALLENEOUS
+        */
+       public final static String DATE_TIME_FORMAT = "dd/MM/yyyy, HH:mm";
+
+       public final static String PARAM_PATH = "org.argeo.jcr.ui.explorer.nodePath";
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPerspective.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPerspective.java
new file mode 100644 (file)
index 0000000..2626415
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.ui.explorer;
+
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+
+/** Base perspective for JcrExplorer browser */
+public class JcrExplorerPerspective implements IPerspectiveFactory {
+       public static String BROWSER_VIEW_ID = JcrExplorerPlugin.ID
+                       + ".browserView";
+
+       public void createInitialLayout(IPageLayout layout) {
+               layout.setEditorAreaVisible(true);
+
+               IFolderLayout upperLeft = layout.createFolder(JcrExplorerPlugin.ID
+                               + ".upperLeft", IPageLayout.LEFT, 0.4f, layout.getEditorArea());
+               upperLeft.addView(BROWSER_VIEW_ID);
+
+//             String editorArea = layout.getEditorArea();
+//             String logViewId = "org.argeo.security.ui.logView";
+//             IFolderLayout bottom = layout.createFolder("bottom",
+//                             IPageLayout.BOTTOM, 0.50f, editorArea);
+//             bottom.addView(logViewId);
+       }
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPlugin.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPlugin.java
new file mode 100644 (file)
index 0000000..3bf9e7f
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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.ui.explorer;
+
+import java.util.ResourceBundle;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class JcrExplorerPlugin extends AbstractUIPlugin {
+       private final static Log log = LogFactory.getLog(JcrExplorerPlugin.class);
+       private ResourceBundle messages;
+
+       // The plug-in ID
+       public static final String ID = "org.argeo.jcr.ui.explorer"; //$NON-NLS-1$
+
+       // The shared instance
+       private static JcrExplorerPlugin plugin;
+
+       /**
+        * The constructor
+        */
+       public JcrExplorerPlugin() {
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see
+        * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext
+        * )
+        */
+       public void start(BundleContext context) throws Exception {
+               super.start(context);
+               plugin = this;
+               messages = ResourceBundle
+                               .getBundle("org.argeo.jcr.ui.explorer.messages");
+
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see
+        * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext
+        * )
+        */
+       public void stop(BundleContext context) throws Exception {
+               plugin = null;
+               super.stop(context);
+       }
+
+       /**
+        * Returns the shared instance
+        * 
+        * @return the shared instance
+        */
+       public static JcrExplorerPlugin getDefault() {
+               return plugin;
+       }
+
+       public static ImageDescriptor getImageDescriptor(String path) {
+               return imageDescriptorFromPlugin(ID, path);
+       }
+
+       /** Returns the internationalized label for the given key */
+       public static String getMessage(String key) {
+               try {
+                       return getDefault().messages.getString(key);
+               } catch (NullPointerException npe) {
+                       log.warn(key + " not found.");
+                       return key;
+               }
+       }
+
+       /**
+        * Gives access to the internationalization message bundle. Returns null in
+        * case the ClientUiPlugin is not started (for JUnit tests, by instance)
+        */
+       public static ResourceBundle getMessagesBundle() {
+               if (getDefault() != null)
+                       // To avoid NPE
+                       return getDefault().messages;
+               else
+                       return null;
+       }
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerView.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerView.java
new file mode 100644 (file)
index 0000000..01a957a
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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.ui.explorer;
+
+import org.argeo.jcr.ui.explorer.views.GenericJcrBrowser;
+
+public class JcrExplorerView extends GenericJcrBrowser {
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeContentProvider.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeContentProvider.java
new file mode 100644 (file)
index 0000000..fb6bd1a
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * 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.ui.explorer.browser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.eclipse.ui.TreeParent;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.RepositoryRegister;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.jcr.ui.explorer.model.RepositoriesElem;
+import org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem;
+import org.argeo.util.security.Keyring;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Implementation of the {@code ITreeContentProvider} to display multiple
+ * repository environment in a tree like structure
+ * 
+ */
+public class NodeContentProvider implements ITreeContentProvider {
+       final private RepositoryRegister repositoryRegister;
+       final private RepositoryFactory repositoryFactory;
+       /**
+        * A session of the logged in user on the default workspace of the node
+        * repository.
+        */
+       final private Session userSession;
+       final private Keyring keyring;
+       private boolean sortChildren;
+
+       // reference for cleaning
+       private SingleJcrNodeElem homeNode = null;
+       private RepositoriesElem repositoriesNode = null;
+
+       // Utils
+       private TreeBrowserComparator itemComparator = new TreeBrowserComparator();
+
+       public NodeContentProvider(Session userSession, Keyring keyring,
+                       RepositoryRegister repositoryRegister,
+                       RepositoryFactory repositoryFactory, Boolean sortChildren) {
+               this.userSession = userSession;
+               this.keyring = keyring;
+               this.repositoryRegister = repositoryRegister;
+               this.repositoryFactory = repositoryFactory;
+               this.sortChildren = sortChildren;
+       }
+
+       public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               if (newInput == null)// dispose
+                       return;
+
+               if (userSession != null) {
+                       Node userHome = UserJcrUtils.getUserHome(userSession);
+                       if (userHome != null) {
+                               // TODO : find a way to dynamically get alias for the node
+                               if (homeNode != null)
+                                       homeNode.dispose();
+                               homeNode = new SingleJcrNodeElem(null, userHome,
+                                               userSession.getUserID(), ArgeoJcrConstants.ALIAS_NODE);
+                       }
+               }
+               if (repositoryRegister != null) {
+                       if (repositoriesNode != null)
+                               repositoriesNode.dispose();
+                       repositoriesNode = new RepositoriesElem("Repositories",
+                                       repositoryRegister, repositoryFactory, null, userSession,
+                                       keyring);
+               }
+       }
+
+       /**
+        * Sends back the first level of the Tree. Independent from inputElement
+        * that can be null
+        */
+       public Object[] getElements(Object inputElement) {
+               List<Object> objs = new ArrayList<Object>();
+               if (homeNode != null)
+                       objs.add(homeNode);
+               if (repositoriesNode != null)
+                       objs.add(repositoriesNode);
+               return objs.toArray();
+       }
+
+       public Object[] getChildren(Object parentElement) {
+               if (parentElement instanceof TreeParent) {
+                       if (sortChildren) {
+                               // TreeParent[] arr = (TreeParent[]) ((TreeParent)
+                               // parentElement)
+                               // .getChildren();
+                               Object[] tmpArr = ((TreeParent) parentElement).getChildren();
+                               TreeParent[] arr = new TreeParent[tmpArr.length];
+                               for (int i = 0; i < tmpArr.length; i++)
+                                       arr[i] = (TreeParent) tmpArr[i];
+
+                               Arrays.sort(arr, itemComparator);
+                               return arr;
+                       } else
+                               return ((TreeParent) parentElement).getChildren();
+
+               } else {
+                       return new Object[0];
+               }
+       }
+
+       /**
+        * Sets whether the content provider should order the children nodes or not.
+        * It is user duty to call a full refresh of the tree after changing this
+        * parameter.
+        */
+       public void setSortChildren(boolean sortChildren) {
+               this.sortChildren = sortChildren;
+       }
+
+       public Object getParent(Object element) {
+               if (element instanceof TreeParent) {
+                       return ((TreeParent) element).getParent();
+               } else
+                       return null;
+       }
+
+       public boolean hasChildren(Object element) {
+               if (element instanceof RepositoriesElem) {
+                       RepositoryRegister rr = ((RepositoriesElem) element)
+                                       .getRepositoryRegister();
+                       return rr.getRepositories().size() > 0;
+               } else if (element instanceof TreeParent) {
+                       TreeParent tp = (TreeParent) element;
+                       return tp.hasChildren();
+               }
+               return false;
+       }
+
+       public void dispose() {
+               if (homeNode != null)
+                       homeNode.dispose();
+               if (repositoriesNode != null) {
+                       // logs out open sessions
+                       // see https://bugzilla.argeo.org/show_bug.cgi?id=23
+                       repositoriesNode.dispose();
+               }
+       }
+
+       /**
+        * Specific comparator for this view. See spec in BUG :
+        * https://www.argeo.org/bugzilla/show_bug.cgi?id=139
+        */
+       private class TreeBrowserComparator implements Comparator<TreeParent> {
+
+               public int category(TreeParent element) {
+                       if (element instanceof SingleJcrNodeElem) {
+                               Node node = ((SingleJcrNodeElem) element).getNode();
+                               try {
+                                       if (node.isNodeType(NodeType.NT_FOLDER))
+                                               return 5;
+                               } catch (RepositoryException e) {
+                                       // TODO Auto-generated catch block
+                                       e.printStackTrace();
+                               }
+                       }
+                       return 10;
+               }
+
+               public int compare(TreeParent o1, TreeParent o2) {
+                       int cat1 = category(o1);
+                       int cat2 = category(o2);
+
+                       if (cat1 != cat2) {
+                               return cat1 - cat2;
+                       }
+                       return o1.getName().compareTo(o2.getName());
+               }
+       }
+}
\ No newline at end of file
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeLabelProvider.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeLabelProvider.java
new file mode 100644 (file)
index 0000000..d6d593c
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.ui.explorer.browser;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.jcr.DefaultNodeLabelProvider;
+import org.argeo.eclipse.ui.jcr.JcrImages;
+import org.argeo.jcr.ui.explorer.model.RemoteRepositoryElem;
+import org.argeo.jcr.ui.explorer.model.RepositoriesElem;
+import org.argeo.jcr.ui.explorer.model.RepositoryElem;
+import org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem;
+import org.argeo.jcr.ui.explorer.model.WorkspaceElem;
+import org.eclipse.swt.graphics.Image;
+
+public class NodeLabelProvider extends DefaultNodeLabelProvider {
+       // Images
+
+       public String getText(Object element) {
+               try {
+                       if (element instanceof SingleJcrNodeElem) {
+                               SingleJcrNodeElem sjn = (SingleJcrNodeElem) element;
+                               return getText(sjn.getNode());
+                       } else
+                               return super.getText(element);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException(
+                                       "Unexpected JCR error while getting node name.");
+               }
+       }
+
+       protected String getText(Node node) throws RepositoryException {
+               String label = node.getName();
+               StringBuffer mixins = new StringBuffer("");
+               for (NodeType type : node.getMixinNodeTypes())
+                       mixins.append(' ').append(type.getName());
+
+               return label + " [" + node.getPrimaryNodeType().getName() + mixins
+                               + "]";
+       }
+
+       @Override
+       public Image getImage(Object element) {
+               if (element instanceof RemoteRepositoryElem) {
+                       if (((RemoteRepositoryElem) element).isConnected())
+                               return JcrImages.REMOTE_CONNECTED;
+                       else
+                               return JcrImages.REMOTE_DISCONNECTED;
+               } else if (element instanceof RepositoryElem) {
+                       if (((RepositoryElem) element).isConnected())
+                               return JcrImages.REPOSITORY_CONNECTED;
+                       else
+                               return JcrImages.REPOSITORY_DISCONNECTED;
+               } else if (element instanceof WorkspaceElem) {
+                       if (((WorkspaceElem) element).isConnected())
+                               return JcrImages.WORKSPACE_CONNECTED;
+                       else
+                               return JcrImages.WORKSPACE_DISCONNECTED;
+               } else if (element instanceof RepositoriesElem) {
+                       return JcrImages.REPOSITORIES;
+               } else if (element instanceof SingleJcrNodeElem)
+                       try {
+                               return super.getImage(((SingleJcrNodeElem) element).getNode());
+                       } catch (RepositoryException e) {
+                               return null;
+                       }
+               return super.getImage(element);
+       }
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/PropertiesContentProvider.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/PropertiesContentProvider.java
new file mode 100644 (file)
index 0000000..d39f319
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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.ui.explorer.browser;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.jcr.utils.JcrItemsComparator;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+public class PropertiesContentProvider implements IStructuredContentProvider {
+       private JcrItemsComparator itemComparator = new JcrItemsComparator();
+
+       public void dispose() {
+       }
+
+       public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+       }
+
+       public Object[] getElements(Object inputElement) {
+               try {
+                       if (inputElement instanceof Node) {
+                               Set<Property> props = new TreeSet<Property>(itemComparator);
+                               PropertyIterator pit = ((Node) inputElement).getProperties();
+                               while (pit.hasNext())
+                                       props.add(pit.nextProperty());
+                               return props.toArray();
+                       }
+                       return new Object[] {};
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot get element for " + inputElement,
+                                       e);
+               }
+       }
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/AddFolderNode.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/AddFolderNode.java
new file mode 100644 (file)
index 0000000..0389aee
--- /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.jcr.ui.explorer.commands;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.eclipse.ui.TreeParent;
+import org.argeo.eclipse.ui.dialogs.SingleValue;
+import org.argeo.eclipse.ui.jcr.JcrUiPlugin;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem;
+import org.argeo.jcr.ui.explorer.model.WorkspaceElem;
+import org.argeo.jcr.ui.explorer.views.GenericJcrBrowser;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * Adds a node of type nt:folder, only on {@link SingleJcrNodeElem} and
+ * {@link WorkspaceElem} TreeObject types.
+ * 
+ * 
+ * This handler assumes that a selection provider is available and picks only
+ * first selected item. It is UI's job to enable the command only when the
+ * selection contains one and only one element. Thus no parameter is passed
+ * through the command.
+ * 
+ * This handler is still 'hard linked' to a GenericJcrBrowser view to enable
+ * correct tree refresh when a node is added. This must be corrected in future
+ * versions.
+ */
+public class AddFolderNode extends AbstractHandler {
+
+       public final static String ID = JcrExplorerPlugin.ID + ".addFolderNode";
+
+       // public final static String DEFAULT_LABEL = JcrExplorerPlugin
+       // .getMessage("addFolderNodeCmdLbl");
+       // public final static String DEFAULT_ICON_REL_PATH = "icons/addRepo.gif";
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+
+               ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage().getSelection();
+
+               GenericJcrBrowser view = (GenericJcrBrowser) HandlerUtil
+                               .getActiveWorkbenchWindow(event).getActivePage()
+                               .findView(HandlerUtil.getActivePartId(event));
+
+               if (selection != null && !selection.isEmpty()
+                               && selection instanceof IStructuredSelection) {
+                       Object obj = ((IStructuredSelection) selection).getFirstElement();
+                       TreeParent treeParentNode = null;
+                       Node jcrParentNode = null;
+
+                       if (obj instanceof SingleJcrNodeElem) {
+                               treeParentNode = (TreeParent) obj;
+                               jcrParentNode = ((SingleJcrNodeElem) treeParentNode).getNode();
+                       } else if (obj instanceof WorkspaceElem) {
+                               treeParentNode = (TreeParent) obj;
+                               jcrParentNode = ((WorkspaceElem) treeParentNode).getRootNode();
+                       } else
+                               return null;
+
+                       String folderName = SingleValue.ask("Folder name",
+                                       "Enter folder name");
+                       if (folderName != null) {
+                               try {
+                                       jcrParentNode.addNode(folderName, NodeType.NT_FOLDER);
+                                       jcrParentNode.getSession().save();
+                                       view.nodeAdded(treeParentNode);
+                               } catch (RepositoryException e) {
+                                       ErrorFeedback.show("Cannot create folder " + folderName
+                                                       + " under " + treeParentNode, e);
+                               }
+                       }
+               } else {
+                       ErrorFeedback.show(JcrUiPlugin
+                                       .getMessage("errorUnvalidNtFolderNodeType"));
+               }
+               return null;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/AddPrivileges.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/AddPrivileges.java
new file mode 100644 (file)
index 0000000..829290e
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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.ui.explorer.commands;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.eclipse.ui.TreeParent;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem;
+import org.argeo.jcr.ui.explorer.model.WorkspaceElem;
+import org.argeo.jcr.ui.explorer.wizards.ChangeRightsWizard;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * Open a dialog to change rights on the selected node.
+ */
+
+public class AddPrivileges extends AbstractHandler {
+       public final static String ID = JcrExplorerPlugin.ID + ".addPrivileges";
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+
+               ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage().getSelection();
+               if (selection != null && !selection.isEmpty()
+                               && selection instanceof IStructuredSelection) {
+                       Object obj = ((IStructuredSelection) selection).getFirstElement();
+                       TreeParent treeParentNode = null;
+                       Node jcrParentNode = null;
+
+                       if (obj instanceof SingleJcrNodeElem) {
+                               treeParentNode = (TreeParent) obj;
+                               jcrParentNode = ((SingleJcrNodeElem) treeParentNode).getNode();
+                       } else if (obj instanceof WorkspaceElem) {
+                               treeParentNode = (TreeParent) obj;
+                               jcrParentNode = ((WorkspaceElem) treeParentNode).getRootNode();
+                       } else
+                               return null;
+
+                       try {
+                               ChangeRightsWizard wizard = new ChangeRightsWizard(
+                                               jcrParentNode.getSession(), jcrParentNode.getPath());
+                               WizardDialog dialog = new WizardDialog(
+                                               HandlerUtil.getActiveShell(event), wizard);
+                               dialog.open();
+                               return null;
+                       } catch (RepositoryException re) {
+                               throw new ArgeoException(
+                                               "Unexpected error while creating the new workspace.",
+                                               re);
+                       }
+               } else {
+                       ErrorFeedback.show("Cannot add privileges");
+               }
+               return null;
+       }
+}
\ No newline at end of file
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/AddRemoteRepository.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/AddRemoteRepository.java
new file mode 100644 (file)
index 0000000..e41edfc
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * 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.ui.explorer.commands;
+
+import java.net.URI;
+import java.util.Hashtable;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.eclipse.ui.utils.CommandUtils;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.jcr.ui.explorer.JcrExplorerConstants;
+import org.argeo.util.security.Keyring;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+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.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Connect to a remote repository and, if successful publish it as an OSGi
+ * service.
+ */
+public class AddRemoteRepository extends AbstractHandler implements
+               JcrExplorerConstants, ArgeoNames {
+
+       private RepositoryFactory repositoryFactory;
+       private Repository nodeRepository;
+       private Keyring keyring;
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               RemoteRepositoryLoginDialog dlg = new RemoteRepositoryLoginDialog(
+                               Display.getDefault().getActiveShell());
+               if (dlg.open() == Dialog.OK) {
+                       CommandUtils.callCommand(Refresh.ID);
+               }
+               return null;
+       }
+
+       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
+               this.repositoryFactory = repositoryFactory;
+       }
+
+       public void setKeyring(Keyring keyring) {
+               this.keyring = keyring;
+       }
+
+       public void setNodeRepository(Repository nodeRepository) {
+               this.nodeRepository = nodeRepository;
+       }
+
+       class RemoteRepositoryLoginDialog extends TitleAreaDialog {
+               private Text name;
+               private Text uri;
+               private Text username;
+               private Text password;
+               private Button saveInKeyring;
+
+               public RemoteRepositoryLoginDialog(Shell parentShell) {
+                       super(parentShell);
+               }
+
+               protected Point getInitialSize() {
+                       return new Point(600, 400);
+               }
+
+               protected Control createDialogArea(Composite parent) {
+                       Composite dialogarea = (Composite) super.createDialogArea(parent);
+                       dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
+                                       true));
+                       Composite composite = new Composite(dialogarea, SWT.NONE);
+                       composite.setLayout(new GridLayout(2, false));
+                       composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
+                                       false));
+                       setMessage("Login to remote repository", IMessageProvider.NONE);
+                       name = createLT(composite, "Name", "remoteRepository");
+                       uri = createLT(composite, "URI",
+                                       "http://localhost:7070/data/jcr/node");
+                       username = createLT(composite, "User", "");
+                       password = createLP(composite, "Password");
+
+                       saveInKeyring = createLC(composite, "Remember password", false);
+                       parent.pack();
+                       return composite;
+               }
+
+               @Override
+               protected void createButtonsForButtonBar(Composite parent) {
+                       super.createButtonsForButtonBar(parent);
+                       Button test = createButton(parent, 2, "Test", false);
+                       test.addSelectionListener(new SelectionAdapter() {
+                               public void widgetSelected(SelectionEvent arg0) {
+                                       testConnection();
+                               }
+                       });
+               }
+
+               void testConnection() {
+                       Session session = null;
+                       try {
+                               URI checkedUri = new URI(uri.getText());
+                               String checkedUriStr = checkedUri.toString();
+
+                               Hashtable<String, String> params = new Hashtable<String, String>();
+                               params.put(ArgeoJcrConstants.JCR_REPOSITORY_URI, checkedUriStr);
+                               Repository repository = repositoryFactory.getRepository(params);
+                               if (username.getText().trim().equals("")) {// anonymous
+                                       session = repository.login();
+                               } else {
+                                       // FIXME use getTextChars() when upgrading to 3.7
+                                       // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=297412
+                                       char[] pwd = password.getText().toCharArray();
+                                       SimpleCredentials sc = new SimpleCredentials(
+                                                       username.getText(), pwd);
+                                       session = repository.login(sc);
+                                       MessageDialog.openInformation(getParentShell(), "Success",
+                                                       "Connection to '" + uri.getText() + "' successful");
+                               }
+                       } catch (Exception e) {
+                               ErrorFeedback.show(
+                                               "Connection test failed for " + uri.getText(), e);
+                       } finally {
+                               JcrUtils.logoutQuietly(session);
+                       }
+               }
+
+               @Override
+               protected void okPressed() {
+                       Session nodeSession = null;
+                       try {
+                               nodeSession = nodeRepository.login();
+                               Node home = UserJcrUtils.getUserHome(nodeSession);
+
+                               Node remote = home.hasNode(ARGEO_REMOTE) ? home
+                                               .getNode(ARGEO_REMOTE) : home.addNode(ARGEO_REMOTE);
+                               if (remote.hasNode(name.getText()))
+                                       throw new ArgeoException(
+                                                       "There is already a remote repository named "
+                                                                       + name.getText());
+                               Node remoteRepository = remote.addNode(name.getText(),
+                                               ArgeoTypes.ARGEO_REMOTE_REPOSITORY);
+                               remoteRepository.setProperty(ARGEO_URI, uri.getText());
+                               remoteRepository.setProperty(ARGEO_USER_ID, username.getText());
+                               nodeSession.save();
+                               if (saveInKeyring.getSelection()) {
+                                       String pwdPath = remoteRepository.getPath() + '/'
+                                                       + ARGEO_PASSWORD;
+                                       keyring.set(pwdPath, password.getText().toCharArray());
+                               }
+                               nodeSession.save();
+                               MessageDialog.openInformation(
+                                               getParentShell(),
+                                               "Repository Added",
+                                               "Remote repository '" + username.getText() + "@"
+                                                               + uri.getText() + "' added");
+
+                               super.okPressed();
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Cannot add remote repository", e);
+                       } finally {
+                               JcrUtils.logoutQuietly(nodeSession);
+                       }
+               }
+
+               /** Creates label and text. */
+               protected Text createLT(Composite parent, String label, String initial) {
+                       new Label(parent, SWT.NONE).setText(label);
+                       Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
+                       text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       text.setText(initial);
+                       return text;
+               }
+
+               /** Creates label and check. */
+               protected Button createLC(Composite parent, String label,
+                               Boolean initial) {
+                       new Label(parent, SWT.NONE).setText(label);
+                       Button check = new Button(parent, SWT.CHECK);
+                       check.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       check.setSelection(initial);
+                       return check;
+               }
+
+               protected Text createLP(Composite parent, String label) {
+                       new Label(parent, SWT.NONE).setText(label);
+                       Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER
+                                       | SWT.PASSWORD);
+                       text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       return text;
+               }
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/CreateWorkspace.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/CreateWorkspace.java
new file mode 100644 (file)
index 0000000..e579630
--- /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.jcr.ui.explorer.commands;
+
+import java.util.Arrays;
+
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.eclipse.ui.dialogs.SingleValue;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.model.RepositoryElem;
+import org.argeo.jcr.ui.explorer.views.GenericJcrBrowser;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/** Creates a new JCR workspace */
+public class CreateWorkspace extends AbstractHandler {
+
+       public final static String ID = JcrExplorerPlugin.ID + ".addFolderNode";
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+
+               ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage().getSelection();
+
+               GenericJcrBrowser view = (GenericJcrBrowser) HandlerUtil
+                               .getActiveWorkbenchWindow(event).getActivePage()
+                               .findView(HandlerUtil.getActivePartId(event));
+
+               if (selection != null && !selection.isEmpty()
+                               && selection instanceof IStructuredSelection) {
+                       Object obj = ((IStructuredSelection) selection).getFirstElement();
+                       if (!(obj instanceof RepositoryElem))
+                               return null;
+
+                       RepositoryElem repositoryNode = (RepositoryElem) obj;
+                       String workspaceName = SingleValue.ask("Workspace name",
+                                       "Enter workspace name");
+                       if (workspaceName != null) {
+                               if (Arrays.asList(repositoryNode.getAccessibleWorkspaceNames())
+                                               .contains(workspaceName)) {
+                                       ErrorFeedback.show("Workspace " + workspaceName
+                                                       + " already exists.");
+                               } else {
+                                       repositoryNode.createWorkspace(workspaceName);
+                                       view.nodeAdded(repositoryNode);
+                               }
+                       }
+               } else {
+                       ErrorFeedback.show("Cannot create workspace");
+               }
+               return null;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/DeleteNodes.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/DeleteNodes.java
new file mode 100644 (file)
index 0000000..26d4cdd
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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.ui.explorer.commands;
+
+import java.util.Iterator;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.eclipse.ui.TreeParent;
+import org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem;
+import org.argeo.jcr.ui.explorer.model.WorkspaceElem;
+import org.argeo.jcr.ui.explorer.views.GenericJcrBrowser;
+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.handlers.HandlerUtil;
+
+/**
+ * Deletes the selected nodes: both in the JCR repository and in the UI view.
+ * Warning no check is done, except implementation dependent native checks,
+ * handle with care.
+ * 
+ * This handler is still 'hard linked' to a GenericJcrBrowser view to enable
+ * correct tree refresh when a node is added. This must be corrected in future
+ * versions.
+ */
+public class DeleteNodes extends AbstractHandler {
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage().getSelection();
+               if (selection == null || !(selection instanceof IStructuredSelection))
+                       return null;
+
+               GenericJcrBrowser view = (GenericJcrBrowser) HandlerUtil
+                               .getActiveWorkbenchWindow(event).getActivePage()
+                               .findView(HandlerUtil.getActivePartId(event));
+
+               // confirmation
+               StringBuffer buf = new StringBuffer("");
+               Iterator<?> lst = ((IStructuredSelection) selection).iterator();
+               while (lst.hasNext()) {
+                       SingleJcrNodeElem sjn = ((SingleJcrNodeElem) lst.next());
+                       buf.append(sjn.getName()).append(' ');
+               }
+               Boolean ok = MessageDialog.openConfirm(
+                               HandlerUtil.getActiveShell(event), "Confirm deletion",
+                               "Do you want to delete " + buf + "?");
+
+               // operation
+               if (ok) {
+                       Iterator<?> it = ((IStructuredSelection) selection).iterator();
+                       Object obj = null;
+                       SingleJcrNodeElem ancestor = null;
+                       WorkspaceElem rootAncestor = null;
+                       try {
+                               while (it.hasNext()) {
+                                       obj = it.next();
+                                       if (obj instanceof SingleJcrNodeElem) {
+                                               // Cache objects
+                                               SingleJcrNodeElem sjn = (SingleJcrNodeElem) obj;
+                                               TreeParent tp = (TreeParent) sjn.getParent();
+                                               Node node = sjn.getNode();
+
+                                               // Jcr Remove
+                                               node.remove();
+                                               node.getSession().save();
+                                               // UI remove
+                                               tp.removeChild(sjn);
+
+                                               // Check if the parent is the root node
+                                               if (tp instanceof WorkspaceElem)
+                                                       rootAncestor = (WorkspaceElem) tp;
+                                               else
+                                                       ancestor = getOlder(ancestor, (SingleJcrNodeElem) tp);
+                                       }
+                               }
+                               if (rootAncestor != null)
+                                       view.nodeRemoved(rootAncestor);
+                               else if (ancestor != null)
+                                       view.nodeRemoved(ancestor);
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Cannot delete selected node ", e);
+                       }
+               }
+               return null;
+       }
+
+       private SingleJcrNodeElem getOlder(SingleJcrNodeElem A, SingleJcrNodeElem B) {
+               try {
+                       if (A == null)
+                               return B == null ? null : B;
+                       // Todo enhanced this method
+                       else
+                               return A.getNode().getDepth() <= B.getNode().getDepth() ? A : B;
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Cannot find ancestor", re);
+               }
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/DumpNode.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/DumpNode.java
new file mode 100644 (file)
index 0000000..c8a235d
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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.ui.explorer.commands;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.specific.OpenFile;
+import org.argeo.eclipse.ui.utils.CommandUtils;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * If the method
+ * <code> HandlerUtil.getActiveWorkbenchWindow(event).getActivePage().getSelection() </code>
+ * exits and has a SingleJcrNodeElem as first element, it canonically calls the
+ * JCR Session.exportSystemView() method on the underlying node with both
+ * skipBinary & noRecurse boolean flags set to false.
+ * 
+ * Resulting stream is saved in a tmp file and opened via the "open file"
+ * single-sourced command.
+ */
+public class DumpNode extends AbstractHandler {
+       public final static String ID = JcrExplorerPlugin.ID + ".dumpNode";
+
+       private final static DateFormat df = new SimpleDateFormat(
+                       "yyyy-MM-dd_HH-mm");
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage().getSelection();
+               if (selection == null || !(selection instanceof IStructuredSelection))
+                       return null;
+
+               Iterator<?> lst = ((IStructuredSelection) selection).iterator();
+               if (lst.hasNext()) {
+                       Object element = lst.next();
+                       if (element instanceof SingleJcrNodeElem) {
+                               SingleJcrNodeElem sjn = (SingleJcrNodeElem) element;
+                               Node node = sjn.getNode();
+
+                               // TODO add a dialog to configure the export and ask for
+                               // confirmation
+                               // Boolean ok = MessageDialog.openConfirm(
+                               // HandlerUtil.getActiveShell(event), "Confirm deletion",
+                               // "Do you want to delete " + buf + "?");
+
+                               File tmpFile;
+                               FileOutputStream fos;
+                               try {
+                                       tmpFile = File.createTempFile("JcrExport", ".xml");
+                                       tmpFile.deleteOnExit();
+                                       fos = new FileOutputStream(tmpFile);
+                                       String dateVal = df.format(new GregorianCalendar()
+                                                       .getTime());
+                                       node.getSession().exportSystemView(node.getPath(), fos,
+                                                       true, false);
+                                       openGeneratedFile(tmpFile.getAbsolutePath(),
+                                                       "Dump-" + JcrUtils.replaceInvalidChars(node.getName())+ "-" + dateVal + ".xml");
+                               } catch (RepositoryException e) {
+                                       throw new ArgeoException(
+                                                       "Unable to perform SystemExport on " + node, e);
+                               } catch (IOException e) {
+                                       throw new ArgeoException("Unable to SystemExport " + node,
+                                                       e);
+                               }
+                       }
+               }
+               return null;
+       }
+
+       private synchronized void openGeneratedFile(String path, String fileName) {
+               Map<String, String> params = new HashMap<String, String>();
+               params.put(OpenFile.PARAM_FILE_NAME, fileName);
+               params.put(OpenFile.PARAM_FILE_URI, "file://" + path);
+               CommandUtils.callCommand("org.argeo.security.ui.specific.openFile",
+                               params);
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/EditNode.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/EditNode.java
new file mode 100644 (file)
index 0000000..e98973e
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.ui.explorer.commands;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.Property;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.eclipse.ui.jcr.editors.NodeEditorInput;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/** Generic command to open a path in an editor. */
+public class EditNode extends AbstractHandler {
+       public final static String EDITOR_PARAM = "editor";
+
+       private String defaultEditorId;
+
+       private Map<String, String> nodeTypeToEditor = new HashMap<String, String>();
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               String path = event.getParameter(Property.JCR_PATH);
+
+               String type = event.getParameter(NodeType.NT_NODE_TYPE);
+               if (type == null)
+                       type = NodeType.NT_UNSTRUCTURED;
+
+               String editorId = event.getParameter(NodeType.NT_NODE_TYPE);
+               if (editorId == null)
+                       editorId = nodeTypeToEditor.containsKey(type) ? nodeTypeToEditor
+                                       .get(type) : defaultEditorId;
+                                       
+               NodeEditorInput nei = new NodeEditorInput(path);
+
+               try {
+                       HandlerUtil.getActiveWorkbenchWindow(event).getActivePage()
+                                       .openEditor(nei, editorId);
+               } catch (PartInitException e) {
+                       ErrorFeedback.show("Cannot open " + editorId + " with " + path
+                                       + " of type " + type, e);
+               }
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       public void setDefaultEditorId(String defaultEditorId) {
+               this.defaultEditorId = defaultEditorId;
+       }
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/GetNodeSize.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/GetNodeSize.java
new file mode 100644 (file)
index 0000000..cfcac13
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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.ui.explorer.commands;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.jcr.Node;
+
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem;
+import org.argeo.jcr.ui.explorer.model.WorkspaceElem;
+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.swt.widgets.Shell;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/** Opens the generic node editor. */
+public class GetNodeSize extends AbstractHandler {
+       // private final static Log log = LogFactory.getLog(GetNodeSize.class);
+
+       public final static String ID = JcrExplorerPlugin.ID + ".getNodeSize";
+
+       // public final static String DEFAULT_ICON_REL_PATH = "icons/getSize.gif";
+       // public final static String DEFAULT_LABEL = JcrExplorerPlugin
+       // .getMessage("getNodeSizeCmdLbl");
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               // JcrUtils.getRepositoryByAlias(repositoryRegister,
+               // ArgeoJcrConstants.ALIAS_NODE);
+
+               ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage().getSelection();
+
+               if (selection != null && !selection.isEmpty()
+                               && selection instanceof IStructuredSelection) {
+
+                       // IStructuredSelection iss = (IStructuredSelection) selection;
+                       // if (iss.size() > 1)
+                       // ErrorFeedback.show(JcrExplorerPlugin
+                       // .getMessage("warningInvalidMultipleSelection"), null);
+
+                       long size = 0;
+
+                       Iterator<?> it = ((IStructuredSelection) selection).iterator();
+
+                       // as the size method is recursive, we keep track of nodes for which
+                       // we already have computed size so that we don't count them twice.
+                       // In a first approximation, we assume that the structure selection
+                       // keep the nodes ordered.
+                       // TODO : enhance that.
+                       List<String> importedPathes = new ArrayList<String>();
+                       try {
+                               nodesIt: while (it.hasNext()) {
+                                       Object obj = it.next();
+                                       String curPath;
+                                       Node node;
+                                       if (obj instanceof SingleJcrNodeElem) {
+                                               node = ((SingleJcrNodeElem) obj).getNode();
+                                               curPath = node.getSession().getWorkspace().getName();
+                                               curPath += "/" + node.getPath();
+                                       } else if (obj instanceof WorkspaceElem) {
+                                               node = ((WorkspaceElem) obj).getRootNode();
+                                               curPath = node.getSession().getWorkspace().getName();
+                                       } else
+                                               // unvalid object type
+                                               continue nodesIt;
+
+                                       Iterator<String> itPath = importedPathes.iterator();
+                                       while (itPath.hasNext()) {
+                                               String refPath = itPath.next();
+                                               if (curPath.startsWith(refPath))
+                                                       // Already done : skip node
+                                                       continue nodesIt;
+                                       }
+                                       size += JcrUtils.getNodeApproxSize(node);
+                                       importedPathes.add(curPath);
+                               }
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Cannot Get size of selected node ", e);
+                       }
+
+                       String[] labels = { "OK" };
+                       Shell shell = HandlerUtil.getActiveWorkbenchWindow(event)
+                                       .getShell();
+                       MessageDialog md = new MessageDialog(shell, "Node size", null,
+                                       "Node size is: " + size / 1024 + " KB",
+                                       MessageDialog.INFORMATION, labels, 0);
+                       md.open();
+               }
+               return null;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/ImportFileSystem.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/ImportFileSystem.java
new file mode 100644 (file)
index 0000000..5c4bf7e
--- /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.jcr.ui.explorer.commands;
+
+import javax.jcr.Node;
+
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.eclipse.ui.TreeParent;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem;
+import org.argeo.jcr.ui.explorer.model.WorkspaceElem;
+import org.argeo.jcr.ui.explorer.views.GenericJcrBrowser;
+import org.argeo.jcr.ui.explorer.wizards.ImportFileSystemWizard;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/** Import a local file system directory tree. */
+public class ImportFileSystem extends AbstractHandler {
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage().getSelection();
+               GenericJcrBrowser view = (GenericJcrBrowser) HandlerUtil
+                               .getActiveWorkbenchWindow(event).getActivePage()
+                               .findView(HandlerUtil.getActivePartId(event));
+               if (selection != null && !selection.isEmpty()
+                               && selection instanceof IStructuredSelection) {
+                       Object obj = ((IStructuredSelection) selection).getFirstElement();
+                       try {
+                               Node folder = null;
+                               if (obj instanceof SingleJcrNodeElem) {
+                                       folder = ((SingleJcrNodeElem) obj).getNode();
+                               } else if (obj instanceof WorkspaceElem) {
+                                       folder = ((WorkspaceElem) obj).getRootNode();
+                               } else {
+                                       ErrorFeedback.show(JcrExplorerPlugin
+                                                       .getMessage("warningInvalidNodeToImport"));
+                               }
+                               if (folder != null) {
+                                       ImportFileSystemWizard wizard = new ImportFileSystemWizard(
+                                                       folder);
+                                       WizardDialog dialog = new WizardDialog(
+                                                       HandlerUtil.getActiveShell(event), wizard);
+                                       dialog.open();
+                                       view.nodeAdded((TreeParent) obj);
+                               }
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Cannot import files to " + obj, e);
+                       }
+               }
+               return null;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/OpenGenericNodeEditor.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/OpenGenericNodeEditor.java
new file mode 100644 (file)
index 0000000..6799d17
--- /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.jcr.ui.explorer.commands;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.jcr.editors.NodeEditorInput;
+import org.argeo.jcr.ui.explorer.JcrExplorerConstants;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.editors.GenericNodeEditor;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/** Opens the generic node editor. */
+public class OpenGenericNodeEditor extends AbstractHandler {
+       public final static String ID = JcrExplorerPlugin.ID + ".openGenericNodeEditor";
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               String path = event.getParameter(JcrExplorerConstants.PARAM_PATH);
+               try {
+                       NodeEditorInput nei = new NodeEditorInput(path);
+                       HandlerUtil.getActiveWorkbenchWindow(event).getActivePage()
+                                       .openEditor(nei, GenericNodeEditor.ID);
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot open editor", e);
+               }
+               return null;
+       }
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/Refresh.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/Refresh.java
new file mode 100644 (file)
index 0000000..21ce256
--- /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.jcr.ui.explorer.commands;
+
+import java.util.Iterator;
+
+import org.argeo.eclipse.ui.TreeParent;
+import org.argeo.eclipse.ui.jcr.views.AbstractJcrBrowser;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.utils.JcrUiUtils;
+import org.argeo.jcr.ui.explorer.views.GenericJcrBrowser;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+
+/**
+ * Force the selected objects of the active view to be refreshed doing the
+ * following:
+ * <ol>
+ * <li>The model objects are recomputed</li>
+ * <li>the view is refreshed</li>
+ * </ol>
+ */
+public class Refresh extends AbstractHandler {
+
+       public final static String ID = JcrExplorerPlugin.ID + ".refresh";
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+
+               AbstractJcrBrowser view = (AbstractJcrBrowser) JcrExplorerPlugin
+                               .getDefault().getWorkbench().getActiveWorkbenchWindow()
+                               .getActivePage().getActivePart();//
+
+               ISelection selection = JcrExplorerPlugin.getDefault().getWorkbench()
+                               .getActiveWorkbenchWindow().getActivePage().getSelection();
+
+               if (selection != null && selection instanceof IStructuredSelection
+                               && !selection.isEmpty()) {
+                       Iterator<?> it = ((IStructuredSelection) selection).iterator();
+                       while (it.hasNext()) {
+                               Object obj = it.next();
+                               if (obj instanceof TreeParent) {
+                                       TreeParent tp = (TreeParent) obj;
+                                       JcrUiUtils.forceRefreshIfNeeded(tp);
+                                       view.refresh(obj);
+                               }
+                       }
+               } else if (view instanceof GenericJcrBrowser)
+                       ((GenericJcrBrowser) view).refresh(null); // force full refresh
+
+               return null;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/RemoveRemoteRepository.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/RemoveRemoteRepository.java
new file mode 100644 (file)
index 0000000..ec23dd0
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.ui.explorer.commands;
+
+import org.argeo.jcr.ui.explorer.model.RemoteRepositoryElem;
+import org.argeo.jcr.ui.explorer.views.GenericJcrBrowser;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/** Remove a registered remote repository */
+public class RemoveRemoteRepository extends AbstractHandler {
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+
+               ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event)
+                               .getActivePage().getSelection();
+
+               GenericJcrBrowser view = (GenericJcrBrowser) HandlerUtil
+                               .getActiveWorkbenchWindow(event).getActivePage()
+                               .findView(HandlerUtil.getActivePartId(event));
+
+               if (selection != null && !selection.isEmpty()
+                               && selection instanceof IStructuredSelection) {
+                       Object obj = ((IStructuredSelection) selection).getFirstElement();
+
+                       if (obj instanceof RemoteRepositoryElem) {
+                               ((RemoteRepositoryElem) obj).remove();
+                               view.refresh(null);
+                       }
+               }
+               return null;
+       }
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/SortChildNodes.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/SortChildNodes.java
new file mode 100644 (file)
index 0000000..2961529
--- /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.jcr.ui.explorer.commands;
+
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.views.GenericJcrBrowser;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.State;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * Change isSorted state of the JcrExplorer Browser
+ */
+public class SortChildNodes extends AbstractHandler {
+       public final static String ID = JcrExplorerPlugin.ID + ".sortChildNodes";
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               GenericJcrBrowser view = (GenericJcrBrowser) HandlerUtil
+                               .getActiveWorkbenchWindow(event).getActivePage()
+                               .findView(GenericJcrBrowser.ID);
+
+               ICommandService service = (ICommandService) PlatformUI.getWorkbench()
+                               .getService(ICommandService.class);
+               Command command = service.getCommand(ID);
+               State state = command.getState(ID + ".toggleState");
+
+               boolean wasSorted = (Boolean) state.getValue();
+               view.setSortChildNodes(!wasSorted);
+               state.setValue(!wasSorted);
+               return null;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/dialogs/ChooseNameDialog.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/dialogs/ChooseNameDialog.java
new file mode 100644 (file)
index 0000000..d4a5cc7
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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.ui.explorer.dialogs;
+
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/** Dialog to change the current user password */
+public class ChooseNameDialog extends TitleAreaDialog {
+       private Text nameT;
+
+       public ChooseNameDialog(Shell parentShell) {
+               super(parentShell);
+               setTitle("Choose name");
+       }
+
+       protected Point getInitialSize() {
+               return new Point(300, 250);
+       }
+
+       protected Control createDialogArea(Composite parent) {
+               Composite dialogarea = (Composite) super.createDialogArea(parent);
+               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               Composite composite = new Composite(dialogarea, SWT.NONE);
+               composite.setLayout(new GridLayout(2, false));
+               composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               nameT = createLT(composite, "Name");
+
+               setMessage("Choose name", IMessageProvider.INFORMATION);
+               parent.pack();
+               return composite;
+       }
+
+       /** Creates label and text. */
+       protected Text createLT(Composite parent, String label) {
+               new Label(parent, SWT.NONE).setText(label);
+               Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
+               text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               return text;
+       }
+
+       public String getName() {
+               return nameT.getText();
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/ChildNodesPage.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/ChildNodesPage.java
new file mode 100644 (file)
index 0000000..682254a
--- /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.ui.explorer.editors;
+
+import javax.jcr.Node;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.browser.NodeLabelProvider;
+import org.argeo.jcr.ui.explorer.providers.SingleNodeAsTreeContentProvider;
+import org.argeo.jcr.ui.explorer.utils.GenericNodeDoubleClickListener;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.FormPage;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+
+/**
+ * List all childs of the current node and brings some browsing capabilities
+ * accross the repository
+ */
+public class ChildNodesPage extends FormPage {
+       // private final static Log log = LogFactory.getLog(ChildNodesPage.class);
+
+       // business objects
+       private Node currentNode;
+
+       // this page UI components
+       private SingleNodeAsTreeContentProvider nodeContentProvider;
+       private TreeViewer nodesViewer;
+
+       public ChildNodesPage(FormEditor editor, String title, Node currentNode) {
+               super(editor, "ChildNodesPage", title);
+               this.currentNode = currentNode;
+       }
+
+       protected void createFormContent(IManagedForm managedForm) {
+               try {
+                       ScrolledForm form = managedForm.getForm();
+                       form.setText(JcrExplorerPlugin.getMessage("childNodesPageTitle"));
+                       Composite body = form.getBody();
+                       GridLayout twt = new GridLayout(1, false);
+                       twt.marginWidth = twt.marginHeight = 5;
+                       body.setLayout(twt);
+                       if (!currentNode.hasNodes()) {
+                               managedForm.getToolkit().createLabel(body,
+                                               JcrExplorerPlugin.getMessage("warningNoChildNode"));
+                       } else {
+
+                               nodeContentProvider = new SingleNodeAsTreeContentProvider();
+                               nodesViewer = createNodeViewer(body, nodeContentProvider);
+                               nodesViewer.setInput(currentNode);
+                       }
+               } catch (Exception e) {
+                       throw new ArgeoException(
+                                       "Unexpected error while creating child node page", e);
+               }
+       }
+
+       protected TreeViewer createNodeViewer(Composite parent,
+                       final ITreeContentProvider nodeContentProvider) {
+
+               final TreeViewer tmpNodeViewer = new TreeViewer(parent, SWT.MULTI);
+
+               tmpNodeViewer.getTree().setLayoutData(
+                               new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               tmpNodeViewer.setContentProvider(nodeContentProvider);
+               tmpNodeViewer.setLabelProvider(new NodeLabelProvider());
+               tmpNodeViewer
+                               .addDoubleClickListener(new GenericNodeDoubleClickListener(
+                                               tmpNodeViewer));
+               return tmpNodeViewer;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/EmptyNodePage.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/EmptyNodePage.java
new file mode 100644 (file)
index 0000000..322c7eb
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.ui.explorer.editors;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.FormPage;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+
+/**
+ * This page is only used at editor's creation time when current node has not
+ * yet been set
+ */
+public class EmptyNodePage extends FormPage {
+       // private final static Log log = LogFactory.getLog(EmptyNodePage.class);
+
+       public EmptyNodePage(FormEditor editor, String title) {
+               super(editor, "Empty Page", title);
+       }
+
+       protected void createFormContent(IManagedForm managedForm) {
+               try {
+                       ScrolledForm form = managedForm.getForm();
+                       GridLayout twt = new GridLayout(1, false);
+                       twt.marginWidth = twt.marginHeight = 0;
+                       form.getBody().setLayout(twt);
+                       Label lbl = new Label(form.getBody(), SWT.NONE);
+                       lbl.setText("Empty page");
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericJcrQueryEditor.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericJcrQueryEditor.java
new file mode 100644 (file)
index 0000000..3b98ed1
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.ui.explorer.editors;
+
+import org.argeo.eclipse.ui.jcr.editors.AbstractJcrQueryEditor;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.eclipse.swt.SWT;
+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.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+
+/** Enables end user to type and execute any JCR query. */
+public class GenericJcrQueryEditor extends AbstractJcrQueryEditor {
+       public final static String ID = JcrExplorerPlugin.ID + ".genericJcrQueryEditor";
+
+       private Text queryField;
+
+       @Override
+       public void createQueryForm(Composite parent) {
+               parent.setLayout(new GridLayout(1, false));
+
+               queryField = new Text(parent, SWT.BORDER | SWT.MULTI | SWT.WRAP);
+               queryField.setText(initialQuery);
+               queryField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               Button execute = new Button(parent, SWT.PUSH);
+               execute.setText("Execute");
+
+               Listener executeListener = new Listener() {
+                       public void handleEvent(Event event) {
+                               executeQuery(queryField.getText());
+                       }
+               };
+
+               execute.addListener(SWT.Selection, executeListener);
+               // queryField.addListener(SWT.DefaultSelection, executeListener);
+       }
+
+       @Override
+       public void setFocus() {
+               queryField.setFocus();
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditor.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditor.java
new file mode 100644 (file)
index 0000000..57f3aa2
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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.ui.explorer.editors;
+
+import javax.jcr.Node;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.forms.editor.FormEditor;
+
+/**
+ * Container for the node editor page. At creation time, it takes a JCR Node
+ * that cannot be changed afterwards.
+ */
+public class GenericNodeEditor extends FormEditor {
+
+       // private final static Log log =
+       // LogFactory.getLog(GenericNodeEditor.class);
+       public final static String ID = JcrExplorerPlugin.ID + ".genericNodeEditor";
+
+       private Node currentNode;
+
+       private GenericPropertyPage genericPropertyPage;
+       private ChildNodesPage childNodesPage;
+       private NodeRightsManagementPage nodeRightsManagementPage;
+       private NodeVersionHistoryPage nodeVersionHistoryPage;
+
+       public void init(IEditorSite site, IEditorInput input)
+                       throws PartInitException {
+               super.init(site, input);
+               GenericNodeEditorInput nei = (GenericNodeEditorInput) getEditorInput();
+               currentNode = nei.getCurrentNode();
+               this.setPartName(JcrUtils.lastPathElement(nei.getPath()));
+       }
+
+       @Override
+       protected void addPages() {
+               try {
+                       // genericNodePage = new GenericNodePage(this,
+                       // JcrExplorerPlugin.getMessage("genericNodePageTitle"),
+                       // currentNode);
+                       // addPage(genericNodePage);
+
+                       genericPropertyPage = new GenericPropertyPage(this,
+                                       JcrExplorerPlugin.getMessage("genericNodePageTitle"),
+                                       currentNode);
+                       addPage(genericPropertyPage);
+
+                       childNodesPage = new ChildNodesPage(this,
+                                       JcrExplorerPlugin.getMessage("childNodesPageTitle"),
+                                       currentNode);
+                       addPage(childNodesPage);
+
+                       nodeRightsManagementPage = new NodeRightsManagementPage(this,
+                                       JcrExplorerPlugin
+                                                       .getMessage("nodeRightsManagementPageTitle"),
+                                       currentNode);
+                       addPage(nodeRightsManagementPage);
+
+                       nodeVersionHistoryPage = new NodeVersionHistoryPage(
+                                       this,
+                                       JcrExplorerPlugin.getMessage("nodeVersionHistoryPageTitle"),
+                                       currentNode);
+                       addPage(nodeVersionHistoryPage);
+               } catch (PartInitException e) {
+                       throw new ArgeoException("Not able to add an empty page ", e);
+               }
+       }
+
+       @Override
+       public void doSaveAs() {
+               // unused compulsory method
+       }
+
+       @Override
+       public void doSave(IProgressMonitor monitor) {
+               try {
+                       // Automatically commit all pages of the editor
+                       commitPages(true);
+                       firePropertyChange(PROP_DIRTY);
+               } catch (Exception e) {
+                       throw new ArgeoException("Error while saving node", e);
+               }
+
+       }
+
+       @Override
+       public boolean isSaveAsAllowed() {
+               return true;
+       }
+
+       Node getCurrentNode() {
+               return currentNode;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditorInput.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditorInput.java
new file mode 100644 (file)
index 0000000..45e337e
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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.ui.explorer.editors;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IPersistableElement;
+
+/**
+ * An editor input based the JCR node object.
+ * */
+
+public class GenericNodeEditorInput implements IEditorInput {
+       private final Node currentNode;
+
+       // cache key properties at creation time to avoid Exception at recoring time
+       // when the session has been closed
+       private String path;
+       private String uid;
+       private String name;
+
+       public GenericNodeEditorInput(Node currentNode) {
+               this.currentNode = currentNode;
+               try {
+                       name = currentNode.getName();
+                       uid = currentNode.getIdentifier();
+                       path = currentNode.getPath();
+               } catch (RepositoryException re) {
+                       throw new ArgeoException(
+                                       "unexpected error while getting node key values at creation time",
+                                       re);
+               }
+       }
+
+       public Node getCurrentNode() {
+               return currentNode;
+       }
+
+       public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+               return null;
+       }
+
+       public boolean exists() {
+               return true;
+       }
+
+       public ImageDescriptor getImageDescriptor() {
+               return null;
+       }
+
+       public String getName() {
+               return name;
+       }
+
+       public String getUid() {
+               return uid;
+       }
+
+       public String getToolTipText() {
+               return path;
+       }
+
+       public String getPath() {
+               return path;
+       }
+
+       public IPersistableElement getPersistable() {
+               return null;
+       }
+
+       /**
+        * equals method based on UID that is unique within a workspace and path of
+        * the node, thus 2 shared node that have same UID as defined in the spec
+        * but 2 different pathes will open two distinct editors.
+        * 
+        * TODO enhance this method to support multirepository and multiworkspace
+        * environments
+        */
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+
+               GenericNodeEditorInput other = (GenericNodeEditorInput) obj;
+               if (!getUid().equals(other.getUid()))
+                       return false;
+               if (!getPath().equals(other.getPath()))
+                       return false;
+               return true;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodePage.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodePage.java
new file mode 100644 (file)
index 0000000..fd4dafe
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * 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.ui.explorer.editors;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ListIterator;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.ui.explorer.JcrExplorerConstants;
+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.Control;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.AbstractFormPart;
+import org.eclipse.ui.forms.IManagedForm;
+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;
+
+/**
+ * Main node editor page. Lists all properties of the current node and enable
+ * access and editing for some of them.
+ */
+
+public class GenericNodePage extends FormPage implements JcrExplorerConstants {
+       // private final static Log log = LogFactory.getLog(GenericNodePage.class);
+
+       // local constants
+       private final static String JCR_PROPERTY_NAME = "jcr:name";
+
+       // Utils
+       protected DateFormat timeFormatter = new SimpleDateFormat(DATE_TIME_FORMAT);
+
+       // Main business Objects
+       private Node currentNode;
+
+       // This page widgets
+       private FormToolkit tk;
+       private List<Control> modifyableProperties = new ArrayList<Control>();
+
+       public GenericNodePage(FormEditor editor, String title, Node currentNode) {
+               super(editor, "id", title);
+               this.currentNode = currentNode;
+       }
+
+       protected void createFormContent(IManagedForm managedForm) {
+               tk = managedForm.getToolkit();
+               ScrolledForm form = managedForm.getForm();
+               GridLayout twt = new GridLayout(3, false);
+               twt.marginWidth = twt.marginHeight = 5;
+
+               form.getBody().setLayout(twt);
+               createPropertiesPart(form.getBody());
+       }
+
+       private void createPropertiesPart(Composite parent) {
+               try {
+
+                       PropertyIterator pi = currentNode.getProperties();
+
+                       // Initializes form part
+                       AbstractFormPart part = new AbstractFormPart() {
+                               public void commit(boolean onSave) {
+                                       try {
+                                               if (onSave) {
+                                                       ListIterator<Control> it = modifyableProperties
+                                                                       .listIterator();
+                                                       while (it.hasNext()) {
+                                                               // we only support Text controls for the time
+                                                               // being
+                                                               Text curControl = (Text) it.next();
+                                                               String value = curControl.getText();
+                                                               currentNode.setProperty((String) curControl
+                                                                               .getData(JCR_PROPERTY_NAME), value);
+                                                       }
+
+                                                       // We only commit when onSave = true,
+                                                       // thus it is still possible to save after a tab
+                                                       // change.
+                                                       super.commit(onSave);
+                                               }
+                                       } catch (RepositoryException re) {
+                                               throw new ArgeoException(
+                                                               "Unexpected error while saving properties", re);
+                                       }
+                               }
+                       };
+
+                       while (pi.hasNext()) {
+                               Property prop = pi.nextProperty();
+                               addPropertyLine(parent, part, prop);
+                       }
+
+                       getManagedForm().addPart(part);
+               } catch (RepositoryException re) {
+                       throw new ArgeoException(
+                                       "Error during creation of network details section", re);
+               }
+
+       }
+
+       private void addPropertyLine(Composite parent, AbstractFormPart part,
+                       Property prop) {
+               try {
+                       tk.createLabel(parent, prop.getName());
+                       tk.createLabel(parent,
+                                       "[" + JcrUtils.getPropertyDefinitionAsString(prop) + "]");
+
+                       if (prop.getDefinition().isProtected()) {
+                               tk.createLabel(parent, formatReadOnlyPropertyValue(prop));
+                       } else
+                               addModifyableValueWidget(parent, part, prop);
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Cannot get property " + prop, re);
+               }
+       }
+
+       private String formatReadOnlyPropertyValue(Property prop) {
+               try {
+                       String strValue;
+
+                       if (prop.getType() == PropertyType.BINARY)
+                               strValue = "<binary>";
+                       else if (prop.isMultiple())
+                               strValue = Arrays.asList(prop.getValues()).toString();
+                       else if (prop.getType() == PropertyType.DATE)
+                               strValue = timeFormatter.format(prop.getValue().getDate()
+                                               .getTime());
+                       else
+                               strValue = prop.getValue().getString();
+
+                       return strValue;
+               } catch (RepositoryException re) {
+                       throw new ArgeoException(
+                                       "Unexpected error while formatting read only property value",
+                                       re);
+               }
+       }
+
+       private Control addModifyableValueWidget(Composite parent,
+                       AbstractFormPart part, Property prop) {
+               GridData gd;
+               try {
+                       if (prop.getType() == PropertyType.STRING) {
+                               Text txt = tk.createText(parent, prop.getString());
+                               gd = new GridData(GridData.FILL_HORIZONTAL);
+                               txt.setLayoutData(gd);
+                               txt.addModifyListener(new ModifiedFieldListener(part));
+                               txt.setData(JCR_PROPERTY_NAME, prop.getName());
+                               modifyableProperties.add(txt);
+                       } else {
+                               // unsupported property type for editing, we create a read only
+                               // label.
+                               return tk
+                                               .createLabel(parent, formatReadOnlyPropertyValue(prop));
+                       }
+                       return null;
+               } catch (RepositoryException re) {
+                       throw new ArgeoException(
+                                       "Unexpected error while formatting read only property value",
+                                       re);
+               }
+
+       }
+
+       //
+       // LISTENERS
+       //
+
+       private class ModifiedFieldListener implements ModifyListener {
+
+               private AbstractFormPart formPart;
+
+               public ModifiedFieldListener(AbstractFormPart generalPart) {
+                       this.formPart = generalPart;
+               }
+
+               public void modifyText(ModifyEvent e) {
+                       formPart.markDirty();
+               }
+       }
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericPropertyPage.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericPropertyPage.java
new file mode 100644 (file)
index 0000000..3aec453
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * 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.ui.explorer.editors;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ui.explorer.JcrExplorerConstants;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.providers.PropertyLabelProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.FormPage;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+
+/**
+ * Generic editor property page. Lists all properties of current node as a
+ * complex tree. TODO: enable editing
+ */
+
+public class GenericPropertyPage extends FormPage implements
+               JcrExplorerConstants {
+       // private final static Log log =
+       // LogFactory.getLog(GenericPropertyPage.class);
+
+       // Main business Objects
+       private Node currentNode;
+
+       public GenericPropertyPage(FormEditor editor, String title, Node currentNode) {
+               super(editor, "id", title);
+               this.currentNode = currentNode;
+       }
+
+       protected void createFormContent(IManagedForm managedForm) {
+               ScrolledForm form = managedForm.getForm();
+               form.setText(JcrExplorerPlugin.getMessage("genericNodePageTitle"));
+               FillLayout layout = new FillLayout();
+               layout.marginHeight = 5;
+               layout.marginWidth = 5;
+               form.getBody().setLayout(layout);
+
+               createComplexTree(form.getBody());
+
+               // TODO remove following
+               // createPropertiesPart(form.getBody());
+       }
+
+       private TreeViewer createComplexTree(Composite parent) {
+               int style = SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION;
+               Tree tree = new Tree(parent, style);
+               createColumn(tree, "Property", SWT.LEFT, 200);
+               createColumn(tree, "Value(s)", SWT.LEFT, 300);
+               createColumn(tree, "Attributes", SWT.LEFT, 65);
+               tree.setLinesVisible(true);
+               tree.setHeaderVisible(true);
+
+               TreeViewer result = new TreeViewer(tree);
+               result.setContentProvider(new TreeContentProvider());
+               result.setLabelProvider(new PropertyLabelProvider());
+               result.setInput(currentNode);
+               result.expandAll();
+               return result;
+       }
+
+       private static TreeColumn createColumn(Tree parent, String name, int style,
+                       int width) {
+               TreeColumn result = new TreeColumn(parent, style);
+               result.setText(name);
+               result.setWidth(width);
+               result.setMoveable(true);
+               result.setResizable(true);
+               return result;
+       }
+
+       //
+       // private void createPropertiesPart(Composite parent) {
+       // try {
+       //
+       // PropertyIterator pi = currentNode.getProperties();
+       //
+       // // Initializes form part
+       // AbstractFormPart part = new AbstractFormPart() {
+       // public void commit(boolean onSave) {
+       // try {
+       // if (onSave) {
+       // ListIterator<Control> it = modifyableProperties
+       // .listIterator();
+       // while (it.hasNext()) {
+       // // we only support Text controls for the time
+       // // being
+       // Text curControl = (Text) it.next();
+       // String value = curControl.getText();
+       // currentNode.setProperty((String) curControl
+       // .getData(JCR_PROPERTY_NAME), value);
+       // }
+       //
+       // // We only commit when onSave = true,
+       // // thus it is still possible to save after a tab
+       // // change.
+       // super.commit(onSave);
+       // }
+       // } catch (RepositoryException re) {
+       // throw new ArgeoException(
+       // "Unexpected error while saving properties", re);
+       // }
+       // }
+       // };
+       //
+       // while (pi.hasNext()) {
+       // Property prop = pi.nextProperty();
+       // addPropertyLine(parent, part, prop);
+       // }
+       //
+       // getManagedForm().addPart(part);
+       // } catch (RepositoryException re) {
+       // throw new ArgeoException(
+       // "Error during creation of network details section", re);
+       // }
+       //
+       // }
+       //
+       // private void addPropertyLine(Composite parent, AbstractFormPart part,
+       // Property prop) {
+       // try {
+       // tk.createLabel(parent, prop.getName());
+       // tk.createLabel(parent,
+       // "[" + JcrUtils.getPropertyDefinitionAsString(prop) + "]");
+       //
+       // if (prop.getDefinition().isProtected()) {
+       // tk.createLabel(parent, formatReadOnlyPropertyValue(prop));
+       // } else
+       // addModifyableValueWidget(parent, part, prop);
+       // } catch (RepositoryException re) {
+       // throw new ArgeoException("Cannot get property " + prop, re);
+       // }
+       // }
+       //
+       // private String formatReadOnlyPropertyValue(Property prop) {
+       // try {
+       // String strValue;
+       //
+       // if (prop.getType() == PropertyType.BINARY)
+       // strValue = "<binary>";
+       // else if (prop.isMultiple())
+       // strValue = Arrays.asList(prop.getValues()).toString();
+       // else if (prop.getType() == PropertyType.DATE)
+       // strValue = timeFormatter.format(prop.getValue().getDate()
+       // .getTime());
+       // else
+       // strValue = prop.getValue().getString();
+       //
+       // return strValue;
+       // } catch (RepositoryException re) {
+       // throw new ArgeoException(
+       // "Unexpected error while formatting read only property value",
+       // re);
+       // }
+       // }
+       //
+       // private Control addModifyableValueWidget(Composite parent,
+       // AbstractFormPart part, Property prop) {
+       // GridData gd;
+       // try {
+       // if (prop.getType() == PropertyType.STRING) {
+       // Text txt = tk.createText(parent, prop.getString());
+       // gd = new GridData(GridData.FILL_HORIZONTAL);
+       // txt.setLayoutData(gd);
+       // txt.addModifyListener(new ModifiedFieldListener(part));
+       // txt.setData(JCR_PROPERTY_NAME, prop.getName());
+       // modifyableProperties.add(txt);
+       // } else {
+       // // unsupported property type for editing, we create a read only
+       // // label.
+       // return tk
+       // .createLabel(parent, formatReadOnlyPropertyValue(prop));
+       // }
+       // return null;
+       // } catch (RepositoryException re) {
+       // throw new ArgeoException(
+       // "Unexpected error while formatting read only property value",
+       // re);
+       // }
+       //
+       // }
+
+       // Multiple Value Model
+       // protected class MultipleValueItem {
+       // private int index;
+       // private Value value;
+       //
+       // public MultipleValueItem(int index, Value value) {
+       // this.index = index;
+       // this.value = value;
+       // }
+       //
+       // public int getIndex() {
+       // return index;
+       // }
+       //
+       // public Object getValue() {
+       // return value;
+       // }
+       // }
+
+       private class TreeContentProvider implements ITreeContentProvider {
+               public Object[] getElements(Object parent) {
+                       Object[] props = null;
+                       try {
+
+                               if (parent instanceof Node) {
+                                       Node node = (Node) parent;
+                                       PropertyIterator pi;
+                                       pi = node.getProperties();
+                                       List<Property> propList = new ArrayList<Property>();
+                                       while (pi.hasNext()) {
+                                               propList.add(pi.nextProperty());
+                                       }
+                                       props = propList.toArray();
+                               }
+                       } catch (RepositoryException e) {
+                               throw new ArgeoException(
+                                               "Unexpected exception while listing node properties", e);
+                       }
+                       return props;
+               }
+
+               public Object getParent(Object child) {
+                       return null;
+               }
+
+               public Object[] getChildren(Object parent) {
+                       Object[] result = null;
+                       if (parent instanceof Property) {
+                               Property prop = (Property) parent;
+                               try {
+
+                                       if (prop.isMultiple()) {
+                                               Value[] values = prop.getValues();
+                                               // List<MultipleValueItem> list = new
+                                               // ArrayList<MultipleValueItem>();
+                                               // for (int i = 0; i < values.length; i++) {
+                                               // MultipleValueItem mvi = new MultipleValueItem(i,
+                                               // values[i]);
+                                               // list.add(mvi);
+                                               // }
+
+                                               return values;
+                                       }
+                               } catch (RepositoryException e) {
+                                       throw new ArgeoException(
+                                                       "Unexpected error getting multiple values property.",
+                                                       e);
+                               }
+                       }
+                       return result;
+               }
+
+               public boolean hasChildren(Object parent) {
+                       try {
+                               if (parent instanceof Property
+                                               && ((Property) parent).isMultiple()) {
+                                       return true;
+                               }
+                       } catch (RepositoryException e) {
+                               throw new ArgeoException(
+                                               "Unexpected exception while checking if property is multiple",
+                                               e);
+                       }
+                       return false;
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+
+               public void dispose() {
+               }
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/NodeRightsManagementPage.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/NodeRightsManagementPage.java
new file mode 100644 (file)
index 0000000..cc5efb5
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * 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.ui.explorer.editors;
+
+import javax.jcr.Node;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.Privilege;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.FormPage;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+
+/**
+ * This comments will be nicely fill by mbaudier in.
+ */
+public class NodeRightsManagementPage extends FormPage {
+       // private final static Log log =
+       // LogFactory.getLog(NodeRightsManagementPage.class);
+
+       private Node currentNode;
+
+       private TableViewer viewer;
+
+       public NodeRightsManagementPage(FormEditor editor, String title,
+                       Node currentNode) {
+               super(editor, "NodeRightsManagementPage", title);
+               this.currentNode = currentNode;
+       }
+
+       protected void createFormContent(IManagedForm managedForm) {
+               ScrolledForm form = managedForm.getForm();
+               form.setText(JcrExplorerPlugin
+                               .getMessage("nodeRightsManagementPageTitle"));
+               FillLayout layout = new FillLayout();
+               layout.marginHeight = 5;
+               layout.marginWidth = 5;
+               form.getBody().setLayout(layout);
+               createRightsPart(form.getBody());
+       }
+
+       /** Creates the rights part */
+       protected void createRightsPart(Composite parent) {
+               Table table = new Table(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+               table.setLinesVisible(true);
+               table.setHeaderVisible(true);
+               viewer = new TableViewer(table);
+
+               // check column
+               TableViewerColumn column = createTableViewerColumn(viewer, "checked",
+                               20);
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       public String getText(Object element) {
+                               return null;
+                       }
+
+                       public Image getImage(Object element) {
+                               return null;
+                       }
+               });
+               // column.setEditingSupport(new RoleEditingSupport(rolesViewer, part));
+
+               // role column
+               column = createTableViewerColumn(viewer, "Role", 200);
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       public String getText(Object element) {
+                               Privilege p = (Privilege) element;
+                               return p.getName();
+                       }
+
+                       public Image getImage(Object element) {
+                               return null;
+                       }
+               });
+               viewer.setContentProvider(new RightsContentProvider());
+               viewer.setInput(getEditorSite());
+       }
+
+       protected TableViewerColumn createTableViewerColumn(TableViewer viewer,
+                       String title, int bound) {
+               final TableViewerColumn viewerColumn = new TableViewerColumn(viewer,
+                               SWT.NONE);
+               final TableColumn column = viewerColumn.getColumn();
+               column.setText(title);
+               column.setWidth(bound);
+               column.setResizable(true);
+               column.setMoveable(true);
+               return viewerColumn;
+       }
+
+       private class RightsContentProvider implements IStructuredContentProvider {
+
+               public void dispose() {
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+
+               public Object[] getElements(Object inputElement) {
+                       try {
+                               AccessControlManager accessControlManager = currentNode
+                                               .getSession().getAccessControlManager();
+                               Privilege[] privileges = accessControlManager
+                                               .getPrivileges(currentNode.getPath());
+                               return privileges;
+                       } catch (Exception e) {
+                               throw new ArgeoException("Cannot retrieve rights", e);
+                       }
+               }
+
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/NodeVersionHistoryPage.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/NodeVersionHistoryPage.java
new file mode 100644 (file)
index 0000000..59fff5e
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * 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.ui.explorer.editors;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionHistory;
+import javax.jcr.version.VersionIterator;
+import javax.jcr.version.VersionManager;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.PropertyDiff;
+import org.argeo.jcr.VersionDiff;
+import org.argeo.jcr.ui.explorer.JcrExplorerConstants;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.providers.FullVersioningTreeContentProvider;
+import org.argeo.jcr.ui.explorer.providers.VersionLabelProvider;
+import org.argeo.jcr.ui.explorer.utils.GenericNodeDoubleClickListener;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.AbstractFormPart;
+import org.eclipse.ui.forms.IManagedForm;
+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.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+/**
+ * Offers two main sections : one to display a text area with a summary of all
+ * variations between a version and its predecessor and one tree view that
+ * enable browsing
+ * */
+public class NodeVersionHistoryPage extends FormPage implements
+               JcrExplorerConstants {
+       // private final static Log log = LogFactory
+       // .getLog(NodeVersionHistoryPage.class);
+
+       // Utils
+       protected DateFormat timeFormatter = new SimpleDateFormat(DATE_TIME_FORMAT);
+
+       // business objects
+       private Node currentNode;
+
+       // this page UI components
+       private FullVersioningTreeContentProvider nodeContentProvider;
+       private TreeViewer nodesViewer;
+       private FormToolkit tk;
+
+       public NodeVersionHistoryPage(FormEditor editor, String title,
+                       Node currentNode) {
+               super(editor, "NodeVersionHistoryPage", title);
+               this.currentNode = currentNode;
+       }
+
+       protected void createFormContent(IManagedForm managedForm) {
+               ScrolledForm form = managedForm.getForm();
+               form.setText(JcrExplorerPlugin
+                               .getMessage("nodeVersionHistoryPageTitle"));
+               tk = managedForm.getToolkit();
+               GridLayout twt = new GridLayout(1, false);
+               twt.marginWidth = twt.marginHeight = 5;
+               Composite body = form.getBody();
+               body.setLayout(twt);
+
+               try {
+                       if (!currentNode.isNodeType(NodeType.MIX_VERSIONABLE)) {
+                               tk.createLabel(body, JcrExplorerPlugin
+                                               .getMessage("warningUnversionableNode"));
+                       } else {
+                               createHistorySection(form.getBody());
+                               createTreeSection(form.getBody());
+                       }
+               } catch (RepositoryException e) {
+                       throw new ArgeoException(
+                                       "Unexpected error while checking if node is versionable", e);
+               }
+       }
+
+       protected void createTreeSection(Composite parent) {
+               // Section Layout & MetaData
+               Section section = tk.createSection(parent, Section.TWISTIE);
+               section.setLayoutData(new GridData(GridData.FILL_BOTH));
+               section.setText(JcrExplorerPlugin.getMessage("versionTreeSectionTitle"));
+
+               // Section Body
+               Composite body = tk.createComposite(section, SWT.FILL);
+               // WARNING : 2 following lines are compulsory or body won't be
+               // displayed.
+               body.setLayout(new GridLayout());
+               section.setClient(body);
+
+               body.setLayoutData(new GridData(GridData.FILL_BOTH));
+               section.setExpanded(true);
+
+               nodeContentProvider = new FullVersioningTreeContentProvider();
+               nodesViewer = createNodeViewer(body, nodeContentProvider);
+               nodesViewer.setInput(currentNode);
+       }
+
+       protected TreeViewer createNodeViewer(Composite parent,
+                       final ITreeContentProvider nodeContentProvider) {
+
+               final TreeViewer tmpNodeViewer = new TreeViewer(parent, SWT.MULTI);
+
+               tmpNodeViewer.getTree().setLayoutData(
+                               new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               tmpNodeViewer.setContentProvider(nodeContentProvider);
+               tmpNodeViewer.setLabelProvider(new VersionLabelProvider());
+               tmpNodeViewer
+                               .addDoubleClickListener(new GenericNodeDoubleClickListener(
+                                               tmpNodeViewer));
+               return tmpNodeViewer;
+       }
+
+       protected void createHistorySection(Composite parent) {
+
+               // Section Layout
+               Section section = tk.createSection(parent, Section.TWISTIE);
+               section.setLayoutData(new GridData(TableWrapData.FILL_GRAB));
+               TableWrapLayout twt = new TableWrapLayout();
+               section.setLayout(twt);
+
+               // Set title of the section
+               section.setText(JcrExplorerPlugin
+                               .getMessage("versionHistorySectionTitle"));
+
+               final Text styledText = tk.createText(section, "", SWT.FULL_SELECTION
+                               | SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
+               styledText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               section.setClient(styledText);
+               refreshHistory(styledText);
+               styledText.setEditable(false);
+               section.setExpanded(false);
+
+               AbstractFormPart part = new AbstractFormPart() {
+                       public void commit(boolean onSave) {
+                       }
+
+                       public void refresh() {
+                               super.refresh();
+                               refreshHistory(styledText);
+                       }
+               };
+               getManagedForm().addPart(part);
+       }
+
+       protected void refreshHistory(Text styledText) {
+               try {
+                       List<VersionDiff> lst = listHistoryDiff();
+                       StringBuffer main = new StringBuffer("");
+
+                       for (int i = lst.size() - 1; i >= 0; i--) {
+                               if (i == 0)
+                                       main.append("Creation (");
+                               else
+                                       main.append("Update " + i + " (");
+
+                               if (lst.get(i).getUserId() != null)
+                                       main.append("UserId : " + lst.get(i).getUserId());
+
+                               if (lst.get(i).getUserId() != null
+                                               && lst.get(i).getUpdateTime() != null)
+                                       main.append(", ");
+
+                               if (lst.get(i).getUpdateTime() != null)
+                                       main.append("Date : "
+                                                       + timeFormatter.format(lst.get(i).getUpdateTime()
+                                                                       .getTime()) + ")\n");
+
+                               StringBuffer buf = new StringBuffer("");
+                               Map<String, PropertyDiff> diffs = lst.get(i).getDiffs();
+                               for (String prop : diffs.keySet()) {
+                                       PropertyDiff pd = diffs.get(prop);
+                                       // String propName = pd.getRelPath();
+                                       Value refValue = pd.getReferenceValue();
+                                       Value newValue = pd.getNewValue();
+                                       String refValueStr = "";
+                                       String newValueStr = "";
+
+                                       if (refValue != null) {
+                                               if (refValue.getType() == PropertyType.DATE) {
+                                                       refValueStr = timeFormatter.format(refValue
+                                                                       .getDate().getTime());
+                                               } else
+                                                       refValueStr = refValue.getString();
+                                       }
+                                       if (newValue != null) {
+                                               if (newValue.getType() == PropertyType.DATE) {
+                                                       newValueStr = timeFormatter.format(newValue
+                                                                       .getDate().getTime());
+                                               } else
+                                                       newValueStr = newValue.getString();
+                                       }
+
+                                       if (pd.getType() == PropertyDiff.MODIFIED) {
+                                               buf.append(prop).append(": ");
+                                               buf.append(refValueStr);
+                                               buf.append(" > ");
+                                               buf.append(newValueStr);
+                                               buf.append("\n");
+                                       } else if (pd.getType() == PropertyDiff.ADDED
+                                                       && !"".equals(newValueStr)) {
+                                               // we don't list property that have been added with an
+                                               // empty string as value
+                                               buf.append(prop).append(": ");
+                                               buf.append(" + ");
+                                               buf.append(newValueStr);
+                                               buf.append("\n");
+                                       } else if (pd.getType() == PropertyDiff.REMOVED) {
+                                               buf.append(prop).append(": ");
+                                               buf.append(" - ");
+                                               buf.append(refValueStr);
+                                               buf.append("\n");
+                                       }
+                               }
+                               buf.append("\n");
+                               main.append(buf);
+                       }
+                       styledText.setText(main.toString());
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot generate history for node", e);
+               }
+
+       }
+
+       public List<VersionDiff> listHistoryDiff() {
+               try {
+                       List<VersionDiff> res = new ArrayList<VersionDiff>();
+                       VersionManager versionManager = currentNode.getSession()
+                                       .getWorkspace().getVersionManager();
+                       VersionHistory versionHistory = versionManager
+                                       .getVersionHistory(currentNode.getPath());
+
+                       VersionIterator vit = versionHistory.getAllLinearVersions();
+                       while (vit.hasNext()) {
+                               Version version = vit.nextVersion();
+                               Node node = version.getFrozenNode();
+                               Version predecessor = null;
+                               try {
+                                       predecessor = version.getLinearPredecessor();
+                               } catch (Exception e) {
+                                       // no predecessor seems to throw an exception even if it
+                                       // shouldn't...
+                               }
+                               if (predecessor == null) {// original
+                               } else {
+                                       Map<String, PropertyDiff> diffs = JcrUtils.diffProperties(
+                                                       predecessor.getFrozenNode(), node);
+                                       if (!diffs.isEmpty()) {
+                                               String lastUserName = null;
+                                               Calendar lastUpdate = null;
+                                               try {
+                                                       if (currentNode
+                                                                       .isNodeType(NodeType.MIX_LAST_MODIFIED)) {
+                                                               lastUserName = node.getProperty(
+                                                                               Property.JCR_LAST_MODIFIED_BY)
+                                                                               .getString();
+                                                               lastUpdate = node.getProperty(
+                                                                               Property.JCR_LAST_MODIFIED).getDate();
+                                                       } else
+                                                               lastUpdate = version.getProperty(
+                                                                               Property.JCR_CREATED).getDate();
+
+                                               } catch (Exception e) {
+                                                       // Silent that info is optional
+                                               }
+                                               VersionDiff vd = new VersionDiff(lastUserName,
+                                                               lastUpdate, diffs);
+                                               res.add(vd);
+                                       }
+                               }
+                       }
+                       return res;
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot generate history for node ");
+               }
+
+       }
+
+       @Override
+       public void setActive(boolean active) {
+               super.setActive(active);
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/StringNodeEditorInput.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/StringNodeEditorInput.java
new file mode 100644 (file)
index 0000000..654b2ae
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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.ui.explorer.editors;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IPersistableElement;
+
+/**
+ * An editor input based on three strings define a node :
+ * <ul>
+ * <li>complete path to the node</li>
+ * <li>the workspace name</li>
+ * <li>the repository alias</li>
+ * </ul>
+ * In a single workspace and/or repository environment, name and alias can be
+ * null.
+ * 
+ * Note : unused for the time being.
+ */
+
+public class StringNodeEditorInput implements IEditorInput {
+       private final String path;
+       private final String repositoryAlias;
+       private final String workspaceName;
+
+       /**
+        * In order to implement a generic explorer that supports remote and multi
+        * workspaces repositories, node path can be detailed by these strings.
+        * 
+        * @param repositoryAlias
+        *            : can be null
+        * @param workspaceName
+        *            : can be null
+        * @param path
+        */
+       public StringNodeEditorInput(String repositoryAlias, String workspaceName,
+                       String path) {
+               this.path = path;
+               this.repositoryAlias = repositoryAlias;
+               this.workspaceName = workspaceName;
+       }
+
+       public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+               return null;
+       }
+
+       public boolean exists() {
+               return true;
+       }
+
+       public ImageDescriptor getImageDescriptor() {
+               return null;
+       }
+
+       public String getName() {
+               return path;
+       }
+
+       public String getRepositoryAlias() {
+               return repositoryAlias;
+       }
+
+       public String getWorkspaceName() {
+               return workspaceName;
+       }
+
+       public IPersistableElement getPersistable() {
+               return null;
+       }
+
+       public String getToolTipText() {
+               return path;
+       }
+
+       public String getPath() {
+               return path;
+       }
+
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+
+               StringNodeEditorInput other = (StringNodeEditorInput) obj;
+
+               if (!path.equals(other.getPath()))
+                       return false;
+
+               String own = other.getWorkspaceName();
+               if ((workspaceName == null && own != null)
+                               || (workspaceName != null && (own == null || !workspaceName
+                                               .equals(own))))
+                       return false;
+
+               String ora = other.getRepositoryAlias();
+               if ((repositoryAlias == null && ora != null)
+                               || (repositoryAlias != null && (ora == null || !repositoryAlias
+                                               .equals(ora))))
+                       return false;
+
+               return true;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/MaintainedRepositoryElem.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/MaintainedRepositoryElem.java
new file mode 100644 (file)
index 0000000..abb97a9
--- /dev/null
@@ -0,0 +1,24 @@
+package org.argeo.jcr.ui.explorer.model;
+
+import javax.jcr.Repository;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.TreeParent;
+import org.argeo.jcr.MaintainedRepository;
+
+/** Wraps a {@link MaintainedRepository} */
+public class MaintainedRepositoryElem extends RepositoryElem {
+
+       public MaintainedRepositoryElem(String alias, Repository repository,
+                       TreeParent parent) {
+               super(alias, repository, parent);
+               if (!(repository instanceof MaintainedRepository)) {
+                       throw new ArgeoException("Repository " + alias
+                                       + " is not amiantained repository");
+               }
+       }
+
+       protected MaintainedRepository getMaintainedRepository() {
+               return (MaintainedRepository) getRepository();
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/RemoteRepositoryElem.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/RemoteRepositoryElem.java
new file mode 100644 (file)
index 0000000..0be6978
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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.ui.explorer.model;
+
+import java.util.Arrays;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.TreeParent;
+import org.argeo.jcr.ArgeoJcrUtils;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.util.security.Keyring;
+
+/** Root of a remote repository */
+public class RemoteRepositoryElem extends RepositoryElem {
+       private final Keyring keyring;
+       /**
+        * A session of the logged in user on the default workspace of the node
+        * repository.
+        */
+       private final Session userSession;
+       private final String remoteNodePath;
+
+       private final RepositoryFactory repositoryFactory;
+       private final String uri;
+
+       public RemoteRepositoryElem(String alias,
+                       RepositoryFactory repositoryFactory, String uri, TreeParent parent,
+                       Session userSession, Keyring keyring, String remoteNodePath) {
+               super(alias, null, parent);
+               this.repositoryFactory = repositoryFactory;
+               this.uri = uri;
+               this.keyring = keyring;
+               this.userSession = userSession;
+               this.remoteNodePath = remoteNodePath;
+       }
+
+       @Override
+       protected Session repositoryLogin(String workspaceName)
+                       throws RepositoryException {
+               Node remoteRepository = userSession.getNode(remoteNodePath);
+               String userID = remoteRepository.getProperty(ArgeoNames.ARGEO_USER_ID)
+                               .getString();
+               String pwdPath = remoteRepository.getPath() + '/'
+                               + ArgeoNames.ARGEO_PASSWORD;
+               char[] password = keyring.getAsChars(pwdPath);
+
+               try {
+                       SimpleCredentials credentials = new SimpleCredentials(userID,
+                                       password);
+                       return getRepository().login(credentials, workspaceName);
+               } finally {
+                       Arrays.fill(password, 0, password.length, ' ');
+               }
+       }
+
+       @Override
+       public Repository getRepository() {
+               if (repository == null)
+                       repository = ArgeoJcrUtils.getRepositoryByUri(repositoryFactory,
+                                       uri);
+               return super.getRepository();
+       }
+
+       public void remove() {
+               try {
+                       Node remoteNode = userSession.getNode(remoteNodePath);
+                       remoteNode.remove();
+                       remoteNode.getSession().save();
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot remove " + remoteNodePath, e);
+               }
+       }
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/RepositoriesElem.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/RepositoriesElem.java
new file mode 100644 (file)
index 0000000..b123727
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * 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.ui.explorer.model;
+
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.eclipse.ui.TreeParent;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.MaintainedRepository;
+import org.argeo.jcr.RepositoryRegister;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.util.security.Keyring;
+
+/**
+ * UI Tree component. Implements the Argeo abstraction of a
+ * {@link RepositoryFactory} that enable a user to "mount" various repositories
+ * in a single Tree like View. It is usually meant to be at the root of the UI
+ * Tree and thus {@link getParent()} method will return null.
+ * 
+ * The {@link RepositoryFactory} is injected at instantiation time and must be
+ * use get or register new {@link Repository} objects upon which a reference is
+ * kept here.
+ */
+
+public class RepositoriesElem extends TreeParent implements ArgeoNames {
+       private final RepositoryRegister repositoryRegister;
+       private final RepositoryFactory repositoryFactory;
+
+       /**
+        * A session of the logged in user on the default workspace of the node
+        * repository.
+        */
+       private final Session userSession;
+       private final Keyring keyring;
+
+       public RepositoriesElem(String name, RepositoryRegister repositoryRegister,
+                       RepositoryFactory repositoryFactory, TreeParent parent,
+                       Session userSession, Keyring keyring) {
+               super(name);
+               this.repositoryRegister = repositoryRegister;
+               this.repositoryFactory = repositoryFactory;
+               this.userSession = userSession;
+               this.keyring = keyring;
+       }
+
+       /**
+        * Override normal behavior to initialize the various repositories only at
+        * request time
+        */
+       @Override
+       public synchronized Object[] getChildren() {
+               if (isLoaded()) {
+                       return super.getChildren();
+               } else {
+                       // initialize current object
+                       Map<String, Repository> refRepos = repositoryRegister
+                                       .getRepositories();
+                       for (String name : refRepos.keySet()) {
+                               Repository repository = refRepos.get(name);
+                               if (repository instanceof MaintainedRepository)
+                                       super.addChild(new MaintainedRepositoryElem(name,
+                                                       repository, this));
+                               else
+                                       super.addChild(new RepositoryElem(name, repository, this));
+                       }
+
+                       // remote
+                       if (keyring != null) {
+                               try {
+                                       addRemoteRepositories(keyring);
+                               } catch (RepositoryException e) {
+                                       throw new ArgeoException(
+                                                       "Cannot browse remote repositories", e);
+                               }
+                       }
+                       return super.getChildren();
+               }
+       }
+
+       protected void addRemoteRepositories(Keyring jcrKeyring)
+                       throws RepositoryException {
+               Node userHome = UserJcrUtils.getUserHome(userSession);
+               if (userHome != null && userHome.hasNode(ARGEO_REMOTE)) {
+                       NodeIterator it = userHome.getNode(ARGEO_REMOTE).getNodes();
+                       while (it.hasNext()) {
+                               Node remoteNode = it.nextNode();
+                               String uri = remoteNode.getProperty(ARGEO_URI).getString();
+                               try {
+                                       RemoteRepositoryElem remoteRepositoryNode = new RemoteRepositoryElem(
+                                                       remoteNode.getName(), repositoryFactory, uri, this,
+                                                       userSession, jcrKeyring, remoteNode.getPath());
+                                       super.addChild(remoteRepositoryNode);
+                               } catch (Exception e) {
+                                       ErrorFeedback.show("Cannot add remote repository "
+                                                       + remoteNode, e);
+                               }
+                       }
+               }
+       }
+
+       public void registerNewRepository(String alias, Repository repository) {
+               // TODO: implement this
+               // Create a new RepositoryNode Object
+               // add it
+               // super.addChild(new RepositoriesNode(...));
+       }
+
+       /** Returns the {@link RepositoryRegister} wrapped by this object. */
+       public RepositoryRegister getRepositoryRegister() {
+               return repositoryRegister;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/RepositoryElem.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/RepositoryElem.java
new file mode 100644 (file)
index 0000000..935bac1
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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.ui.explorer.model;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.TreeParent;
+
+/**
+ * UI Tree component. Wraps a JCR {@link Repository}. It also keeps a reference
+ * to its parent Tree Ui component; typically the unique {@link Repositories}
+ * object of the current view to enable bi-directionnal browsing in the tree.
+ */
+
+public class RepositoryElem extends TreeParent {
+       private String alias;
+       protected Repository repository;
+       private Session defaultSession = null;
+
+       /** Create a new repository with distinct name & alias */
+       public RepositoryElem(String alias, Repository repository, TreeParent parent) {
+               super(alias);
+               this.repository = repository;
+               setParent(parent);
+               this.alias = alias;
+       }
+
+       public void login() {
+               try {
+                       defaultSession = repositoryLogin(null);
+                       String[] wkpNames = defaultSession.getWorkspace()
+                                       .getAccessibleWorkspaceNames();
+                       for (String wkpName : wkpNames) {
+                               if (wkpName.equals(defaultSession.getWorkspace().getName()))
+                                       addChild(new WorkspaceElem(this, wkpName, defaultSession));
+                               else
+                                       addChild(new WorkspaceElem(this, wkpName));
+                       }
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot connect to repository " + alias, e);
+               }
+       }
+
+       /**
+        * Actual call to the
+        * {@link Repository#login(javax.jcr.Credentials, String)} method. To be
+        * overridden.
+        */
+       protected Session repositoryLogin(String workspaceName)
+                       throws RepositoryException {
+               return repository.login(workspaceName);
+       }
+
+       public String[] getAccessibleWorkspaceNames() {
+               try {
+                       return defaultSession.getWorkspace().getAccessibleWorkspaceNames();
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot retrieve workspace names", e);
+               }
+       }
+
+       public void createWorkspace(String workspaceName) {
+               if (!isConnected())
+                       login();
+               try {
+                       defaultSession.getWorkspace().createWorkspace(workspaceName);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot create workspace", e);
+               }
+       }
+
+       /** returns the {@link Repository} referenced by the current UI Node */
+       public Repository getRepository() {
+               return repository;
+       }
+
+       public String getAlias() {
+               return alias;
+       }
+
+       public Boolean isConnected() {
+               if (defaultSession != null && defaultSession.isLive())
+                       return true;
+               else
+                       return false;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/SingleJcrNodeElem.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/SingleJcrNodeElem.java
new file mode 100644 (file)
index 0000000..7b588f8
--- /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.jcr.ui.explorer.model;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Workspace;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.TreeParent;
+
+/**
+ * UI Tree component. Wraps a node of a JCR {@link Workspace}. It also keeps a
+ * reference to its parent node that can either be a {@link WorkspaceElem}, a
+ * {@link SingleJcrNodeElem} or null if the node is "mounted" as the root of the UI
+ * tree.
+ */
+
+public class SingleJcrNodeElem extends TreeParent {
+
+       private final Node node;
+       private String alias = null;
+
+       // keeps a local reference to the node's name to avoid exception when the
+       // session is lost
+       // private final String name;
+
+       /** Creates a new UiNode in the UI Tree */
+       public SingleJcrNodeElem(TreeParent parent, Node node, String name) {
+               super(name);
+               setParent(parent);
+               this.node = node;
+       }
+
+       /**
+        * Creates a new UiNode in the UI Tree, keeping a reference to the alias of
+        * the corresponding repository in the current UI environment. It is useful
+        * to be able to mount nodes as roots of the UI tree.
+        */
+       public SingleJcrNodeElem(TreeParent parent, Node node, String name, String alias) {
+               super(name);
+               setParent(parent);
+               this.node = node;
+               this.alias = alias;
+       }
+
+       /** returns the node wrapped by the current Ui object */
+       public Node getNode() {
+               return node;
+       }
+
+       protected String getRepositoryAlias() {
+               return alias;
+       }
+
+       /**
+        * Override normal behavior to initialize children only when first requested
+        */
+       @Override
+       public synchronized Object[] getChildren() {
+               if (isLoaded()) {
+                       return super.getChildren();
+               } else {
+                       // initialize current object
+                       try {
+                               NodeIterator ni = node.getNodes();
+                               while (ni.hasNext()) {
+                                       Node curNode = ni.nextNode();
+                                       addChild(new SingleJcrNodeElem(this, curNode, curNode.getName()));
+                               }
+                               return super.getChildren();
+                       } catch (RepositoryException re) {
+                               throw new ArgeoException(
+                                               "Unexcpected error while initializing children SingleJcrNode",
+                                               re);
+                       }
+               }
+       }
+
+       @Override
+       public boolean hasChildren() {
+               try {
+                       if (node.getSession().isLive())
+                               return node.hasNodes();
+                       else
+                               return false;
+               } catch (RepositoryException re) {
+                       throw new ArgeoException(
+                                       "Unexpected error while checking children node existence",
+                                       re);
+               }
+       }
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/WorkspaceElem.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/WorkspaceElem.java
new file mode 100644 (file)
index 0000000..bb9b69c
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * 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.ui.explorer.model;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Workspace;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.TreeParent;
+import org.argeo.jcr.JcrUtils;
+
+/**
+ * UI Tree component. Wraps the root node of a JCR {@link Workspace}. It also
+ * keeps a reference to its parent {@link RepositoryElem}, to be able to
+ * retrieve alias of the current used repository
+ */
+public class WorkspaceElem extends TreeParent {
+       private Session session = null;
+
+       public WorkspaceElem(RepositoryElem parent, String name) {
+               this(parent, name, null);
+       }
+
+       public WorkspaceElem(RepositoryElem parent, String name, Session session) {
+               super(name);
+               this.session = session;
+               setParent(parent);
+       }
+
+       public Session getSession() {
+               return session;
+       }
+
+       public Node getRootNode() {
+               try {
+                       if (session != null)
+                               return session.getRootNode();
+                       else
+                               return null;
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot get root node of workspace "
+                                       + getName(), e);
+               }
+       }
+
+       public void login() {
+               try {
+                       session = ((RepositoryElem) getParent()).repositoryLogin(getName());
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot connect to repository "
+                                       + getName(), e);
+               }
+       }
+
+       public Boolean isConnected() {
+               if (session != null && session.isLive())
+                       return true;
+               else
+                       return false;
+       }
+
+       @Override
+       public synchronized void dispose() {
+               logout();
+               super.dispose();
+       }
+
+       /** Logouts the session, does not nothing if there is no live session. */
+       public void logout() {
+               clearChildren();
+               JcrUtils.logoutQuietly(session);
+       }
+
+       @Override
+       public boolean hasChildren() {
+               try {
+                       if (isConnected())
+                               return session.getRootNode().hasNodes();
+                       else
+                               return false;
+               } catch (RepositoryException re) {
+                       throw new ArgeoException(
+                                       "Unexpected error while checking children node existence",
+                                       re);
+               }
+       }
+
+       /** Override normal behaviour to initialize display of the workspace */
+       @Override
+       public synchronized Object[] getChildren() {
+               if (isLoaded()) {
+                       return super.getChildren();
+               } else {
+                       // initialize current object
+                       try {
+                               Node rootNode;
+                               if (session == null)
+                                       return null;
+                               else
+                                       rootNode = session.getRootNode();
+                               NodeIterator ni = rootNode.getNodes();
+                               while (ni.hasNext()) {
+                                       Node node = ni.nextNode();
+                                       addChild(new SingleJcrNodeElem(this, node, node.getName()));
+                               }
+                               return super.getChildren();
+                       } catch (RepositoryException e) {
+                               throw new ArgeoException(
+                                               "Cannot initialize WorkspaceNode UI object."
+                                                               + getName(), e);
+                       }
+               }
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/providers/FullVersioningTreeContentProvider.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/providers/FullVersioningTreeContentProvider.java
new file mode 100644 (file)
index 0000000..736a5aa
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.ui.explorer.providers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionHistory;
+import javax.jcr.version.VersionIterator;
+import javax.jcr.version.VersionManager;
+
+import org.argeo.ArgeoException;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Implementation of the {@code ITreeContentProvider} in order to display some
+ * version informations of a JCR full versionable node in a tree like structure
+ * 
+ */
+public class FullVersioningTreeContentProvider implements ITreeContentProvider {
+       // private Node rootNode;
+       // private ItemComparator itemComparator = new ItemComparator();
+
+       /**
+        * Sends back the first level of the Tree. input element must be a single
+        * node object
+        */
+       public Object[] getElements(Object inputElement) {
+               try {
+                       Node rootNode = (Node) inputElement;
+                       String curPath = rootNode.getPath();
+                       VersionManager vm = rootNode.getSession().getWorkspace()
+                                       .getVersionManager();
+
+                       VersionHistory vh = vm.getVersionHistory(curPath);
+                       List<Version> result = new ArrayList<Version>();
+                       VersionIterator vi = vh.getAllLinearVersions();
+
+                       while (vi.hasNext()) {
+                               result.add(vi.nextVersion());
+                       }
+                       return result.toArray();
+               } catch (RepositoryException re) {
+                       throw new ArgeoException(
+                                       "Unexpected error while getting version elements", re);
+               }
+       }
+
+       public Object[] getChildren(Object parentElement) {
+               try {
+                       if (parentElement instanceof Version) {
+                               List<Node> tmp = new ArrayList<Node>();
+                               tmp.add(((Version) parentElement).getFrozenNode());
+                               return tmp.toArray();
+                       }
+               } catch (RepositoryException re) {
+                       throw new ArgeoException("Unexpected error while getting child "
+                                       + "node for version element", re);
+               }
+               return null;
+       }
+
+       public Object getParent(Object element) {
+               try {
+                       // this will not work in a simpleVersionning environment, parent is
+                       // not a node.
+                       if (element instanceof Node
+                                       && ((Node) element).isNodeType(NodeType.NT_FROZEN_NODE)) {
+                               Node node = (Node) element;
+                               return node.getParent();
+                       } else
+                               return null;
+               } catch (RepositoryException e) {
+                       return null;
+               }
+       }
+
+       public boolean hasChildren(Object element) {
+               try {
+                       if (element instanceof Version)
+                               return true;
+                       else if (element instanceof Node)
+                               return ((Node) element).hasNodes();
+                       else
+                               return false;
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot check children of " + element, e);
+               }
+       }
+
+       public void dispose() {
+       }
+
+       public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+       }
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/providers/PropertyLabelProvider.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/providers/PropertyLabelProvider.java
new file mode 100644 (file)
index 0000000..decbe63
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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.ui.explorer.providers;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.ui.explorer.JcrExplorerConstants;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ViewerCell;
+
+public class PropertyLabelProvider extends ColumnLabelProvider implements
+               JcrExplorerConstants {
+
+       // To be able to change column order easily
+       public static final int COLUMN_PROPERTY = 0;
+       public static final int COLUMN_VALUE = 1;
+       public static final int COLUMN_ATTRIBUTES = 2;
+
+       // Utils
+       protected DateFormat timeFormatter = new SimpleDateFormat(DATE_TIME_FORMAT);
+
+       public void update(ViewerCell cell) {
+               Object element = cell.getElement();
+               cell.setText(getColumnText(element, cell.getColumnIndex()));
+               // Image image = getImage(element);
+               // cell.setImage(image);
+               // cell.setBackground(getBackground(element));
+               // cell.setForeground(getForeground(element));
+               // cell.setFont(getFont(element));
+       }
+
+       public String getColumnText(Object element, int columnIndex) {
+               try {
+                       if (element instanceof Property) {
+                               Property prop = (Property) element;
+                               if (prop.isMultiple()) {
+                                       switch (columnIndex) {
+                                       case COLUMN_PROPERTY:
+                                               return prop.getName();
+                                       case COLUMN_VALUE:
+                                               // Corresponding values are listed on children
+                                               return "";
+                                       case COLUMN_ATTRIBUTES:
+                                               return JcrUtils.getPropertyDefinitionAsString(prop);
+                                       }
+                               } else {
+                                       switch (columnIndex) {
+                                       case COLUMN_PROPERTY:
+                                               return prop.getName();
+                                       case COLUMN_VALUE:
+                                               return formatValueAsString(prop.getValue());
+                                       case COLUMN_ATTRIBUTES:
+                                               return JcrUtils.getPropertyDefinitionAsString(prop);
+                                       }
+                               }
+                       } else if (element instanceof Value) {
+                               Value val = (Value) element;
+
+                               switch (columnIndex) {
+                               case COLUMN_PROPERTY:
+                                       // Nothing to show
+                                       return "";
+                               case COLUMN_VALUE:
+                                       return formatValueAsString(val);
+                               case COLUMN_ATTRIBUTES:
+                                       // Corresponding attributes are listed on the parent
+                                       return "";
+                               }
+                       }
+
+               } catch (RepositoryException re) {
+                       throw new ArgeoException(
+                                       "Unexepected error while getting property values", re);
+               }
+               return null;
+       }
+
+       private String formatValueAsString(Value value) {
+               // TODO enhance this method
+               try {
+                       String strValue;
+
+                       if (value.getType() == PropertyType.BINARY)
+                               strValue = "<binary>";
+                       else if (value.getType() == PropertyType.DATE)
+                               strValue = timeFormatter.format(value.getDate().getTime());
+                       else
+                               strValue = value.getString();
+                       return strValue;
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("unexpected error while formatting value",
+                                       e);
+               }
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/providers/SingleNodeAsTreeContentProvider.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/providers/SingleNodeAsTreeContentProvider.java
new file mode 100644 (file)
index 0000000..8c69b6b
--- /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.jcr.ui.explorer.providers;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.jcr.utils.JcrItemsComparator;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Implementation of the {@code ITreeContentProvider} in order to display a
+ * single JCR node and its children in a tree like structure
+ * 
+ */
+public class SingleNodeAsTreeContentProvider implements ITreeContentProvider {
+       // private Node rootNode;
+       private JcrItemsComparator itemComparator = new JcrItemsComparator();
+
+       /**
+        * Sends back the first level of the Tree. input element must be a single
+        * node object
+        */
+       public Object[] getElements(Object inputElement) {
+               try {
+                       Node rootNode = (Node) inputElement;
+                       List<Node> result = new ArrayList<Node>();
+                       NodeIterator ni = rootNode.getNodes();
+                       while (ni.hasNext()) {
+                               result.add(ni.nextNode());
+                       }
+
+                       return result.toArray();
+               } catch (RepositoryException re) {
+                       throw new ArgeoException(
+                                       "Unexpected error while getting child nodes for children editor page ",
+                                       re);
+               }
+       }
+
+       public Object[] getChildren(Object parentElement) {
+               return childrenNodes((Node) parentElement);
+       }
+
+       public Object getParent(Object element) {
+               try {
+                       Node node = (Node) element;
+                       if (!node.getPath().equals("/"))
+                               return node.getParent();
+                       else
+                               return null;
+               } catch (RepositoryException e) {
+                       return null;
+               }
+       }
+
+       public boolean hasChildren(Object element) {
+               try {
+                       return ((Node) element).hasNodes();
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot check children of " + element, e);
+               }
+       }
+
+       public void dispose() {
+       }
+
+       public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+       }
+
+       protected Object[] childrenNodes(Node parentNode) {
+               try {
+                       List<Node> children = new ArrayList<Node>();
+                       NodeIterator nit = parentNode.getNodes();
+                       while (nit.hasNext()) {
+                               Node node = nit.nextNode();
+                               children.add(node);
+                       }
+                       Node[] arr = children.toArray(new Node[children.size()]);
+                       Arrays.sort(arr, itemComparator);
+                       return arr;
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot list children of " + parentNode, e);
+               }
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/providers/VersionLabelProvider.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/providers/VersionLabelProvider.java
new file mode 100644 (file)
index 0000000..69b35eb
--- /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.jcr.ui.explorer.providers;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.version.Version;
+
+import org.argeo.ArgeoException;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+
+/**
+ * simple wrapping of the ColumnLabelProvider class to provide text display in
+ * order to build a tree for version. The Get text method does not assume that
+ * Version extends Node class to respect JCR 2.0 specification
+ * 
+ */
+public class VersionLabelProvider extends ColumnLabelProvider {
+
+       public String getText(Object element) {
+               try {
+                       if (element instanceof Version) {
+                               Version version = (Version) element;
+                               return version.getName();
+                       } else if (element instanceof Node) {
+                               return ((Node) element).getName();
+                       }
+               } catch (RepositoryException re) {
+                       throw new ArgeoException(
+                                       "Unexpected error while getting element name", re);
+               }
+               return super.getText(element);
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/GenericNodeDoubleClickListener.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/GenericNodeDoubleClickListener.java
new file mode 100644 (file)
index 0000000..0f93450
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * 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.ui.explorer.utils;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.jcr.utils.JcrFileProvider;
+import org.argeo.eclipse.ui.specific.FileHandler;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.editors.GenericNodeEditor;
+import org.argeo.jcr.ui.explorer.editors.GenericNodeEditorInput;
+import org.argeo.jcr.ui.explorer.model.RepositoryElem;
+import org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem;
+import org.argeo.jcr.ui.explorer.model.WorkspaceElem;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.ui.PartInitException;
+
+/**
+ * Centralizes the management of double click on a NodeTreeViewer
+ */
+public class GenericNodeDoubleClickListener implements IDoubleClickListener {
+
+       // private final static Log log = LogFactory
+       // .getLog(GenericNodeDoubleClickListener.class);
+
+       private TreeViewer nodeViewer;
+       private JcrFileProvider jfp;
+       private FileHandler fileHandler;
+
+       public GenericNodeDoubleClickListener(TreeViewer nodeViewer) {
+               this.nodeViewer = nodeViewer;
+               jfp = new JcrFileProvider();
+               // Commented out. see https://www.argeo.org/bugzilla/show_bug.cgi?id=188
+               fileHandler = null;
+               // fileHandler = new FileHandler(jfp);
+       }
+
+       public void doubleClick(DoubleClickEvent event) {
+               if (event.getSelection() == null || event.getSelection().isEmpty())
+                       return;
+               Object obj = ((IStructuredSelection) event.getSelection())
+                               .getFirstElement();
+               if (obj instanceof RepositoryElem) {
+                       RepositoryElem rpNode = (RepositoryElem) obj;
+                       if (!rpNode.isConnected()) {
+                               rpNode.login();
+                               nodeViewer.refresh(obj);
+                       }
+                       // else do nothing
+               } else if (obj instanceof WorkspaceElem) {
+                       WorkspaceElem wn = (WorkspaceElem) obj;
+                       if (wn.isConnected())
+                               wn.logout();
+                       else
+                               wn.login();
+                       nodeViewer.refresh(obj);
+               } else if (obj instanceof SingleJcrNodeElem) {
+                       SingleJcrNodeElem sjn = (SingleJcrNodeElem) obj;
+                       Node node = sjn.getNode();
+                       try {
+                               if (node.isNodeType(NodeType.NT_FILE)) {
+                                       // double click on a file node triggers its opening
+                                       String name = node.getName();
+                                       String id = node.getIdentifier();
+
+                                       // For the file provider to be able to browse the
+                                       // various
+                                       // repository.
+                                       // TODO : enhanced that.
+                                       // ITreeContentProvider itcp = (ITreeContentProvider)
+                                       // nodeViewer
+                                       // .getContentProvider();
+                                       jfp.setReferenceNode(node);
+                                       if (fileHandler != null)
+                                               fileHandler.openFile(name, id);
+                               }
+                               GenericNodeEditorInput gnei = new GenericNodeEditorInput(node);
+                               JcrExplorerPlugin.getDefault().getWorkbench()
+                                               .getActiveWorkbenchWindow().getActivePage()
+                                               .openEditor(gnei, GenericNodeEditor.ID);
+                       } catch (RepositoryException re) {
+                               throw new ArgeoException(
+                                               "Repository error while getting node info", re);
+                       } catch (PartInitException pie) {
+                               throw new ArgeoException(
+                                               "Unexepected exception while opening node editor", pie);
+                       }
+               }
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/JcrUiUtils.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/JcrUiUtils.java
new file mode 100644 (file)
index 0000000..ca32113
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * 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.ui.explorer.utils;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.TreeParent;
+import org.argeo.jcr.ui.explorer.model.RepositoriesElem;
+import org.argeo.jcr.ui.explorer.model.RepositoryElem;
+import org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem;
+import org.argeo.jcr.ui.explorer.model.WorkspaceElem;
+
+/** Centralizes some useful methods to build UIs with JCR */
+public class JcrUiUtils {
+
+       /** Insure that the UI component is not stale, refresh if needed */
+       public static void forceRefreshIfNeeded(TreeParent element) {
+               Node curNode = null;
+
+               boolean doRefresh = false;
+
+               try {
+                       if (element instanceof SingleJcrNodeElem) {
+                               curNode = ((SingleJcrNodeElem) element).getNode();
+                       } else if (element instanceof WorkspaceElem) {
+                               curNode = ((WorkspaceElem) element).getRootNode();
+                       }
+
+                       if (curNode != null
+                                       && element.getChildren().length != curNode.getNodes()
+                                                       .getSize())
+                               doRefresh = true;
+                       else if (element instanceof RepositoryElem) {
+                               RepositoryElem rn = (RepositoryElem) element;
+                               if (rn.isConnected()) {
+                                       String[] wkpNames = rn.getAccessibleWorkspaceNames();
+                                       if (element.getChildren().length != wkpNames.length)
+                                               doRefresh = true;
+                               }
+                       } else if (element instanceof RepositoriesElem) {
+                               doRefresh = true;
+                               // Always force refresh for RepositoriesElem : the condition
+                               // below does not take remote repository into account and it is
+                               // not trivial to do so.
+
+                               // RepositoriesElem rn = (RepositoriesElem) element;
+                               // if (element.getChildren().length !=
+                               // rn.getRepositoryRegister()
+                               // .getRepositories().size())
+                               // doRefresh = true;
+                       }
+                       if (doRefresh) {
+                               element.clearChildren();
+                               element.getChildren();
+                       }
+               } catch (RepositoryException re) {
+                       throw new ArgeoException(
+                                       "Unexpected error while synchronising the UI with the JCR repository",
+                                       re);
+               }
+       }
+
+       /**
+        * Insure that a model element is inline with the underlying data by
+        * cleaning the corresponding subtree and building it again.
+        */
+       public static void forceRebuild(TreeParent element) {
+               // TODO implement this method if needed.
+       }
+       /**
+        * Workaround to get the alias of the repository that contains the given
+        * element. As we cannot browse the UI tree upward we recursively browse it
+        * downward until we find the given element
+        * */
+       // public static String getRepositoryAliasFromITreeElement(
+       // NodeContentProvider ncp, Object element) {
+       // RepositoryNode repositoryNode = null;
+       // if (element instanceof RepositoryNode)
+       // return ((RepositoryNode) element).getName();
+       // else if (element instanceof RepositoryRegister)
+       // throw new ArgeoException(
+       // "Cannot get alias for a repository register");
+       //
+       // // Get root elements
+       // Object[] elements = ncp.getElements(null);
+       //
+       // try {
+       // for (int i = 0; i < elements.length; i++) {
+       // if (elements[i] instanceof Node) {
+       // Node curNode = (Node) elements[i];
+       // if (curNode.isNodeType(ArgeoTypes.ARGEO_USER_HOME)) {
+       // // Do nothing, we'll find the node in the "normal" tree
+       // // and
+       // // get corresponding alias this way round
+       // } else
+       // throw new ArgeoException(
+       // "Normal nodes should not be at the root of NodeTreeViewer");
+       // } else if (elements[i] instanceof RepositoryRegister) {
+       // RepositoryRegister repositoryRegister = (RepositoryRegister) elements[i];
+       // Map<String, Repository> repositories = repositoryRegister
+       // .getRepositories();
+       //
+       // for (String name : repositories.keySet()) {
+       // boolean found = isElementInCurrentTreePart(
+       // ncp,
+       // new RepositoryNode(name, repositories.get(name)),
+       // (Node) element);
+       // if (found)
+       // return name;
+       // }
+       // } else
+       // throw new ArgeoException(
+       // "Unexpected object class at the root of NodeTreeViewer");
+       // }
+       // } catch (RepositoryException re) {
+       // throw new ArgeoException(
+       // "Unexpected error while retrieving Alias name", re);
+       // }
+       // return null;
+       // }
+       //
+       // /** implements the recursivity */
+       // private static boolean isElementInCurrentTreePart(NodeContentProvider
+       // ncp,
+       // Object parentElement, NodParente searchedElement) {
+       // boolean found = false;
+       // if (parentElement instanceof WorkspaceNode) {
+       // WorkspaceNode wn = (WorkspaceNode) parentElement;
+       // Object[] children = wn.getChildren();
+       // int i = children.length - 1;
+       // while (!found && i >= 0) {
+       // found = isElementInCurrentTreePart(ncp, children[i],
+       // searchedElement);
+       // }
+       // return found;
+       // } else if (parentElement instanceof RepositoryNode) {
+       // RepositoryNode rn = (RepositoryNode) parentElement;
+       // Object[] children = rn.getChildren();
+       // int i = children.length - 1;
+       // while (!found && i >= 0) {
+       // found = isElementInCurrentTreePart(ncp, children[i],
+       // searchedElement);
+       // }
+       // return found;
+       // } else {
+       // Node node = (Node) parentElement;
+       // if (node.equals(searchedElement))
+       // return true;
+       // NodeIterator ni;
+       // try {
+       // ni = node.getNodes();
+       // while (!found && ni.hasNext()) {
+       // found = isElementInCurrentTreePart(ncp, ni.nextNode(),
+       // searchedElement);
+       // }
+       // } catch (RepositoryException e) {
+       // throw new ArgeoException("unexpected erreur while recursively"
+       // + " recovering RepositoryNode for selected object", e);
+       // }
+       //
+       // return found;
+       // }
+       // }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/TreeObjectsComparator.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/TreeObjectsComparator.java
new file mode 100644 (file)
index 0000000..02450d6
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.ui.explorer.utils;
+
+import java.util.Comparator;
+
+import org.argeo.eclipse.ui.TreeParent;
+
+public class TreeObjectsComparator implements Comparator<TreeParent> {
+       public int compare(TreeParent o1, TreeParent o2) {
+               return o1.getName().compareTo(o2.getName());
+       }
+}
\ No newline at end of file
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/views/GenericJcrBrowser.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/views/GenericJcrBrowser.java
new file mode 100644 (file)
index 0000000..f401ae8
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * 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.ui.explorer.views;
+
+import java.util.List;
+
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventListener;
+import javax.jcr.observation.ObservationManager;
+
+import org.argeo.ArgeoException;
+import org.argeo.eclipse.ui.TreeParent;
+import org.argeo.eclipse.ui.jcr.AsyncUiEventListener;
+import org.argeo.eclipse.ui.jcr.utils.NodeViewerComparer;
+import org.argeo.eclipse.ui.jcr.views.AbstractJcrBrowser;
+import org.argeo.jcr.RepositoryRegister;
+import org.argeo.jcr.ui.explorer.JcrExplorerPlugin;
+import org.argeo.jcr.ui.explorer.browser.NodeContentProvider;
+import org.argeo.jcr.ui.explorer.browser.NodeLabelProvider;
+import org.argeo.jcr.ui.explorer.browser.PropertiesContentProvider;
+import org.argeo.jcr.ui.explorer.model.SingleJcrNodeElem;
+import org.argeo.jcr.ui.explorer.utils.GenericNodeDoubleClickListener;
+import org.argeo.jcr.ui.explorer.utils.JcrUiUtils;
+import org.argeo.util.security.Keyring;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+
+/**
+ * Basic View to display a sash form to browse a JCR compliant multirepository
+ * environment
+ */
+public class GenericJcrBrowser extends AbstractJcrBrowser {
+       public final static String ID = JcrExplorerPlugin.ID + ".browserView";
+       private boolean sortChildNodes = true;
+
+       /* DEPENDENCY INJECTION */
+       private Keyring keyring;
+       private RepositoryRegister repositoryRegister;
+       private RepositoryFactory repositoryFactory;
+       private Repository nodeRepository;
+       /**
+        * A session of the logged in user on the default workspace of the node
+        * repository.
+        */
+       private Session userSession;
+
+       // This page widgets
+       private TreeViewer nodesViewer;
+       private NodeContentProvider nodeContentProvider;
+       private TableViewer propertiesViewer;
+       private EventListener resultsObserver;
+
+       @Override
+       public void createPartControl(Composite parent) {
+               parent.setLayout(new FillLayout());
+               SashForm sashForm = new SashForm(parent, SWT.VERTICAL);
+               sashForm.setSashWidth(4);
+               sashForm.setLayout(new FillLayout());
+
+               // Create the tree on top of the view
+               Composite top = new Composite(sashForm, SWT.NONE);
+               GridLayout gl = new GridLayout(1, false);
+               top.setLayout(gl);
+
+               try {
+                       this.userSession = this.nodeRepository.login();
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot open user session", e);
+               }
+
+               nodeContentProvider = new NodeContentProvider(userSession, keyring,
+                               repositoryRegister, repositoryFactory, sortChildNodes);
+
+               // nodes viewer
+               nodesViewer = createNodeViewer(top, nodeContentProvider);
+
+               // context menu : it is completely defined in the plugin.xml file.
+               MenuManager menuManager = new MenuManager();
+               Menu menu = menuManager.createContextMenu(nodesViewer.getTree());
+
+               nodesViewer.getTree().setMenu(menu);
+               getSite().registerContextMenu(menuManager, nodesViewer);
+               getSite().setSelectionProvider(nodesViewer);
+
+               nodesViewer.setInput(getViewSite());
+
+               // Create the property viewer on the bottom
+               Composite bottom = new Composite(sashForm, SWT.NONE);
+               bottom.setLayout(new GridLayout(1, false));
+               propertiesViewer = createPropertiesViewer(bottom);
+
+               sashForm.setWeights(getWeights());
+               nodesViewer.setComparer(new NodeViewerComparer());
+       }
+
+       @Override
+       public void refresh(Object obj) {
+               // Enable full refresh from a command when no element of the tree is
+               // selected
+               if (obj == null) {
+                       Object[] elements = nodeContentProvider.getElements(null);
+                       for (Object el : elements) {
+                               if (el instanceof TreeParent)
+                                       JcrUiUtils.forceRefreshIfNeeded((TreeParent) el);
+                               getNodeViewer().refresh(el);
+                       }
+               }
+               super.refresh(obj);
+       }
+
+       /**
+        * To be overridden to adapt size of form and result frames.
+        */
+       protected int[] getWeights() {
+               return new int[] { 70, 30 };
+       }
+
+       protected TreeViewer createNodeViewer(Composite parent,
+                       final ITreeContentProvider nodeContentProvider) {
+
+               final TreeViewer tmpNodeViewer = new TreeViewer(parent, SWT.MULTI);
+
+               tmpNodeViewer.getTree().setLayoutData(
+                               new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               tmpNodeViewer.setContentProvider(nodeContentProvider);
+               tmpNodeViewer.setLabelProvider(new NodeLabelProvider());
+               tmpNodeViewer
+                               .addSelectionChangedListener(new ISelectionChangedListener() {
+                                       public void selectionChanged(SelectionChangedEvent event) {
+                                               if (!event.getSelection().isEmpty()) {
+                                                       IStructuredSelection sel = (IStructuredSelection) event
+                                                                       .getSelection();
+                                                       Object firstItem = sel.getFirstElement();
+                                                       if (firstItem instanceof SingleJcrNodeElem)
+                                                               propertiesViewer
+                                                                               .setInput(((SingleJcrNodeElem) firstItem)
+                                                                                               .getNode());
+                                               } else {
+                                                       propertiesViewer.setInput(getViewSite());
+                                               }
+                                       }
+                               });
+
+               resultsObserver = new TreeObserver(tmpNodeViewer.getTree().getDisplay());
+               if (keyring != null)
+                       try {
+                               ObservationManager observationManager = userSession
+                                               .getWorkspace().getObservationManager();
+                               observationManager.addEventListener(resultsObserver,
+                                               Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED, "/",
+                                               true, null, null, false);
+                       } catch (RepositoryException e) {
+                               throw new ArgeoException("Cannot register listeners", e);
+                       }
+
+               tmpNodeViewer
+                               .addDoubleClickListener(new GenericNodeDoubleClickListener(
+                                               tmpNodeViewer));
+               return tmpNodeViewer;
+       }
+
+       protected TableViewer createPropertiesViewer(Composite parent) {
+               propertiesViewer = new TableViewer(parent);
+               propertiesViewer.getTable().setLayoutData(
+                               new GridData(SWT.FILL, SWT.FILL, true, true));
+               propertiesViewer.getTable().setHeaderVisible(true);
+               propertiesViewer.setContentProvider(new PropertiesContentProvider());
+               TableViewerColumn col = new TableViewerColumn(propertiesViewer,
+                               SWT.NONE);
+               col.getColumn().setText("Name");
+               col.getColumn().setWidth(200);
+               col.setLabelProvider(new ColumnLabelProvider() {
+                       public String getText(Object element) {
+                               try {
+                                       return ((Property) element).getName();
+                               } catch (RepositoryException e) {
+                                       throw new ArgeoException(
+                                                       "Unexpected exception in label provider", e);
+                               }
+                       }
+               });
+               col = new TableViewerColumn(propertiesViewer, SWT.NONE);
+               col.getColumn().setText("Value");
+               col.getColumn().setWidth(400);
+               col.setLabelProvider(new ColumnLabelProvider() {
+                       public String getText(Object element) {
+                               try {
+                                       Property property = (Property) element;
+                                       if (property.getType() == PropertyType.BINARY)
+                                               return "<binary>";
+                                       else if (property.isMultiple()) {
+                                               StringBuffer buf = new StringBuffer("[");
+                                               Value[] values = property.getValues();
+                                               for (int i = 0; i < values.length; i++) {
+                                                       if (i != 0)
+                                                               buf.append(", ");
+                                                       buf.append(values[i].getString());
+                                               }
+                                               buf.append(']');
+                                               return buf.toString();
+                                       } else
+                                               return property.getValue().getString();
+                               } catch (RepositoryException e) {
+                                       throw new ArgeoException(
+                                                       "Unexpected exception in label provider", e);
+                               }
+                       }
+               });
+               col = new TableViewerColumn(propertiesViewer, SWT.NONE);
+               col.getColumn().setText("Type");
+               col.getColumn().setWidth(200);
+               col.setLabelProvider(new ColumnLabelProvider() {
+                       public String getText(Object element) {
+                               try {
+                                       return PropertyType.nameFromValue(((Property) element)
+                                                       .getType());
+                               } catch (RepositoryException e) {
+                                       throw new ArgeoException(
+                                                       "Unexpected exception in label provider", e);
+                               }
+                       }
+               });
+               propertiesViewer.setInput(getViewSite());
+               return propertiesViewer;
+       }
+
+       @Override
+       public void dispose() {
+               super.dispose();
+       }
+
+       @Override
+       protected TreeViewer getNodeViewer() {
+               return nodesViewer;
+       }
+
+       /**
+        * Resets the tree content provider
+        * 
+        * @param sortChildNodes
+        *            if true the content provider will use a comparer to sort nodes
+        *            that might slow down the display
+        * */
+       public void setSortChildNodes(boolean sortChildNodes) {
+               this.sortChildNodes = sortChildNodes;
+               ((NodeContentProvider) nodesViewer.getContentProvider())
+                               .setSortChildren(sortChildNodes);
+               nodesViewer.setInput(getViewSite());
+       }
+
+       /** Notifies the current view that a node has been added */
+       public void nodeAdded(TreeParent parentNode) {
+               // insure that Ui objects have been correctly created:
+               JcrUiUtils.forceRefreshIfNeeded(parentNode);
+               getNodeViewer().refresh(parentNode);
+               getNodeViewer().expandToLevel(parentNode, 1);
+       }
+
+       /** Notifies the current view that a node has been removed */
+       public void nodeRemoved(TreeParent parentNode) {
+               IStructuredSelection newSel = new StructuredSelection(parentNode);
+               getNodeViewer().setSelection(newSel, true);
+               // Force refresh
+               IStructuredSelection tmpSel = (IStructuredSelection) getNodeViewer()
+                               .getSelection();
+               getNodeViewer().refresh(tmpSel.getFirstElement());
+       }
+
+       class TreeObserver extends AsyncUiEventListener {
+
+               public TreeObserver(Display display) {
+                       super(display);
+               }
+
+               @Override
+               protected Boolean willProcessInUiThread(List<Event> events)
+                               throws RepositoryException {
+                       for (Event event : events) {
+                               if (getLog().isTraceEnabled())
+                                       getLog().debug("Received event " + event);
+                               String path = event.getPath();
+                               int index = path.lastIndexOf('/');
+                               String propertyName = path.substring(index + 1);
+                               if (getLog().isTraceEnabled())
+                                       getLog().debug("Concerned property " + propertyName);
+                       }
+                       return false;
+               }
+
+               protected void onEventInUiThread(List<Event> events)
+                               throws RepositoryException {
+                       if (getLog().isTraceEnabled())
+                               getLog().trace("Refresh result list");
+                       nodesViewer.refresh();
+               }
+
+       }
+
+       public boolean getSortChildNodes() {
+               return sortChildNodes;
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setRepositoryRegister(RepositoryRegister repositoryRegister) {
+               this.repositoryRegister = repositoryRegister;
+       }
+
+       public void setKeyring(Keyring keyring) {
+               this.keyring = keyring;
+       }
+
+       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
+               this.repositoryFactory = repositoryFactory;
+       }
+
+       public void setNodeRepository(Repository nodeRepository) {
+               this.nodeRepository = nodeRepository;
+       }
+
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/wizards/ChangeRightsWizard.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/wizards/ChangeRightsWizard.java
new file mode 100644 (file)
index 0000000..4381837
--- /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.jcr.ui.explorer.wizards;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.jface.wizard.Wizard;
+
+/**
+ * Small wizard to manage authorizations on the root node of the current
+ * workspace
+ */
+public class ChangeRightsWizard extends Wizard {
+
+       private Session currentSession;
+       private String path;
+
+       // This page widget
+       private ChooseRightsPage page;
+
+       public ChangeRightsWizard(Session currentSession, String path) {
+               super();
+               this.currentSession = currentSession;
+               this.path = path;
+       }
+
+       @Override
+       public void addPages() {
+               try {
+                       page = new ChooseRightsPage(path);
+                       addPage(page);
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot add page to wizard ", e);
+               }
+       }
+
+       @Override
+       public boolean performFinish() {
+               if (!canFinish())
+                       return false;
+               try {
+                       JcrUtils.addPrivilege(currentSession, path, page.getGroupName(),
+                                       page.getAuthTypeStr());
+               } catch (RepositoryException re) {
+                       throw new ArgeoException(
+                                       "Unexpected error while setting privileges", re);
+               }
+               return true;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/wizards/ChooseRightsPage.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/wizards/ChooseRightsPage.java
new file mode 100644 (file)
index 0000000..69948ce
--- /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.jcr.ui.explorer.wizards;
+
+import javax.jcr.security.Privilege;
+
+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;
+
+public class ChooseRightsPage extends WizardPage implements ModifyListener {
+
+       // This page widget
+       private Text groupNameTxt;
+       private Combo authorizationCmb;
+
+       // USABLE SHORTCUTS
+       protected final static String[] validAuthType = { Privilege.JCR_READ,
+                       Privilege.JCR_WRITE, Privilege.JCR_ALL };
+
+       public ChooseRightsPage(String path) {
+               super("Main");
+               setTitle("Add privilege to " + path);
+       }
+
+       public void createControl(Composite parent) {
+               // specify subject
+               Composite composite = new Composite(parent, SWT.NONE);
+               composite.setLayout(new GridLayout(2, false));
+               Label lbl = new Label(composite, SWT.LEAD);
+               lbl.setText("Group or user name (no blank, no special chars)");
+               lbl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
+               groupNameTxt = new Text(composite, SWT.LEAD | SWT.BORDER);
+               groupNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
+                               false));
+               if (groupNameTxt != null)
+                       groupNameTxt.addModifyListener(this);
+
+               // Choose rigths
+               new Label(composite, SWT.NONE).setText("Choose corresponding rights");
+               authorizationCmb = new Combo(composite, SWT.BORDER | SWT.V_SCROLL);
+               authorizationCmb.setItems(validAuthType);
+               GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+               authorizationCmb.setLayoutData(gd);
+
+               authorizationCmb.select(0);
+
+               // Compulsory
+               setControl(composite);
+       }
+
+       protected String getGroupName() {
+               return groupNameTxt.getText();
+       }
+
+       protected String getAuthTypeStr() {
+               return authorizationCmb.getItem(authorizationCmb.getSelectionIndex());
+       }
+
+       public void modifyText(ModifyEvent event) {
+               String message = checkComplete();
+               if (message != null)
+                       setMessage(message, WizardPage.ERROR);
+               else {
+                       setMessage("Complete", WizardPage.INFORMATION);
+                       setPageComplete(true);
+               }
+       }
+
+       /** @return error message or null if complete */
+       protected String checkComplete() {
+               String groupStr = groupNameTxt.getText();
+               if (groupStr == null || "".equals(groupStr))
+                       return "Please enter the name of the corresponding group.";
+               // Remove regexp check for the time being.
+               // else if (!match(groupStr))
+               // return
+               // "Please use only alphanumerical chars for the short technical name.";
+               return null;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/wizards/ImportFileSystemWizard.java b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/wizards/ImportFileSystemWizard.java
new file mode 100644 (file)
index 0000000..127c2cd
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * 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.ui.explorer.wizards;
+
+import java.io.File;
+import java.io.FileInputStream;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.nodetype.NodeType;
+
+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.argeo.ArgeoException;
+import org.argeo.eclipse.ui.ErrorFeedback;
+import org.argeo.eclipse.ui.specific.ImportToServerWizardPage;
+import org.argeo.eclipse.ui.specific.UploadFileWizardPage;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.wizard.Wizard;
+
+public class ImportFileSystemWizard extends Wizard {
+       private final static Log log = LogFactory
+                       .getLog(ImportFileSystemWizard.class);
+
+       private UploadFileWizardPage importPage;
+       private final Node folder;
+
+       public ImportFileSystemWizard(Node folder) {
+               this.folder = folder;
+               setWindowTitle("Import from file system");
+       }
+
+       @Override
+       public void addPages() {
+               importPage = new UploadFileWizardPage();
+               addPage(importPage);
+               setNeedsProgressMonitor(importPage.getNeedsProgressMonitor());
+       }
+
+       /**
+        * Called when the user click on 'Finish' in the wizard. The real upload to
+        * the JCR repository is done here.
+        */
+       @Override
+       public boolean performFinish() {
+
+               // Initialization
+               final String objectType = importPage.getObjectType();
+               final String objectPath = importPage.getObjectPath();
+
+               // We do not display a progress bar for one file only
+               if (ImportToServerWizardPage.FILE_ITEM_TYPE.equals(objectType)) {
+                       // In Rap we must force the "real" upload of the file
+                       importPage.performFinish();
+                       try {
+                               Node fileNode = folder.addNode(importPage.getObjectName(),
+                                               NodeType.NT_FILE);
+                               Node resNode = fileNode.addNode(Property.JCR_CONTENT,
+                                               NodeType.NT_RESOURCE);
+                               Binary binary = null;
+                               try {
+                                       binary = folder.getSession().getValueFactory()
+                                                       .createBinary(importPage.getFileInputStream());
+                                       resNode.setProperty(Property.JCR_DATA, binary);
+                               } finally {
+                                       if (binary != null)
+                                               binary.dispose();
+                                       IOUtils.closeQuietly(importPage.getFileInputStream());
+                               }
+                               folder.getSession().save();
+                       } catch (Exception e) {
+                               e.printStackTrace();
+                               return false;
+                       }
+                       return true;
+               } else if (ImportToServerWizardPage.FOLDER_ITEM_TYPE.equals(objectType)) {
+                       if (objectPath == null || !new File(objectPath).exists()) {
+                               ErrorFeedback.show("Directory " + objectPath
+                                               + " does not exist");
+                               return false;
+                       }
+
+                       Boolean failed = false;
+                       final File dir = new File(objectPath).getAbsoluteFile();
+                       final Long sizeB = directorySize(dir, 0l);
+                       final Stats stats = new Stats();
+                       Long begin = System.currentTimeMillis();
+                       try {
+                               getContainer().run(true, true, new IRunnableWithProgress() {
+                                       public void run(IProgressMonitor monitor) {
+                                               try {
+                                                       Integer sizeKB = (int) (sizeB / FileUtils.ONE_KB);
+                                                       monitor.beginTask("", sizeKB);
+                                                       importDirectory(folder, dir, monitor, stats);
+                                                       monitor.done();
+                                               } catch (Exception e) {
+                                                       if (e instanceof RuntimeException)
+                                                               throw (RuntimeException) e;
+                                                       else
+                                                               throw new ArgeoException("Cannot import "
+                                                                               + objectPath, e);
+                                               }
+                                       }
+                               });
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Cannot import " + objectPath, e);
+                               failed = true;
+                       }
+
+                       Long duration = System.currentTimeMillis() - begin;
+                       Long durationS = duration / 1000l;
+                       String durationStr = (durationS / 60) + " min " + (durationS % 60)
+                                       + " s";
+                       StringBuffer message = new StringBuffer("Imported\n");
+                       message.append(stats.fileCount).append(" files\n");
+                       message.append(stats.dirCount).append(" directories\n");
+                       message.append(FileUtils.byteCountToDisplaySize(stats.sizeB));
+                       if (failed)
+                               message.append(" of planned ").append(
+                                               FileUtils.byteCountToDisplaySize(sizeB));
+                       message.append("\n");
+                       message.append("in ").append(durationStr).append("\n");
+                       if (failed)
+                               MessageDialog.openError(getShell(), "Import failed",
+                                               message.toString());
+                       else
+                               MessageDialog.openInformation(getShell(), "Import successful",
+                                               message.toString());
+
+                       return true;
+               }
+               return false;
+
+       }
+
+       /** Recursively computes the size of the directory in bytes. */
+       protected Long directorySize(File dir, Long currentSize) {
+               Long size = currentSize;
+               File[] files = dir.listFiles();
+               for (File file : files) {
+                       if (file.isDirectory()) {
+                               size = directorySize(file, size);
+                       } else {
+                               size = size + file.length();
+                       }
+               }
+               return size;
+       }
+
+       /**
+        * Import recursively a directory and its content to the repository.
+        */
+       protected void importDirectory(Node folder, File dir,
+                       IProgressMonitor monitor, Stats stats) {
+               try {
+                       File[] files = dir.listFiles();
+                       for (File file : files) {
+                               if (file.isDirectory()) {
+                                       Node childFolder = folder.addNode(file.getName(),
+                                                       NodeType.NT_FOLDER);
+                                       importDirectory(childFolder, file, monitor, stats);
+                                       folder.getSession().save();
+                                       stats.dirCount++;
+                               } else {
+                                       Long fileSize = file.length();
+
+                                       // we skip tempory files that are created by apps when a
+                                       // file is being edited.
+                                       // TODO : make this configurable.
+                                       if (file.getName().lastIndexOf('~') != file.getName()
+                                                       .length() - 1) {
+
+                                               monitor.subTask(file.getName() + " ("
+                                                               + FileUtils.byteCountToDisplaySize(fileSize)
+                                                               + ") " + file.getCanonicalPath());
+                                               try {
+                                                       Node fileNode = folder.addNode(file.getName(),
+                                                                       NodeType.NT_FILE);
+                                                       Node resNode = fileNode.addNode(
+                                                                       Property.JCR_CONTENT, NodeType.NT_RESOURCE);
+                                                       Binary binary = null;
+                                                       try {
+                                                               binary = folder
+                                                                               .getSession()
+                                                                               .getValueFactory()
+                                                                               .createBinary(new FileInputStream(file));
+                                                               resNode.setProperty(Property.JCR_DATA, binary);
+                                                       } finally {
+                                                               if (binary != null)
+                                                                       binary.dispose();
+                                                       }
+                                                       folder.getSession().save();
+                                                       stats.fileCount++;
+                                                       stats.sizeB = stats.sizeB + fileSize;
+                                               } catch (Exception e) {
+                                                       log.warn("Import of "
+                                                                       + file
+                                                                       + " ("
+                                                                       + FileUtils
+                                                                                       .byteCountToDisplaySize(fileSize)
+                                                                       + ") failed: " + e);
+                                                       folder.getSession().refresh(false);
+                                               }
+                                               monitor.worked((int) (fileSize / FileUtils.ONE_KB));
+                                       }
+                               }
+                       }
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot import " + dir + " to " + folder,
+                                       e);
+               }
+       }
+
+       static class Stats {
+               public Long fileCount = 0l;
+               public Long dirCount = 0l;
+               public Long sizeB = 0l;
+       }
+}
diff --git a/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/resources/org/argeo/jcr/ui/explorer/messages.properties b/trunk/server/plugins/org.argeo.jcr.ui.explorer/src/main/resources/org/argeo/jcr/ui/explorer/messages.properties
new file mode 100644 (file)
index 0000000..3023c52
--- /dev/null
@@ -0,0 +1,28 @@
+## English labels for Agreo JCR UI application 
+
+## Generic labels 
+
+## Errors & warnings 
+errorUnvalidNtFolderNodeType= Error: folder can only be created on a Jcr Node
+warningInvalidNodeToImport=Can only import to a node
+warningInvalidMultipleSelection=This functionality is implemented only on a single node for the time being.
+warningUnversionableNode= Current node is not versionable.
+warningNoChildNode= Current node has no child.
+
+## Commands 
+getNodeSizeCmdLbl= Get approx. size
+addFolderNodeCmdLbl= Add Folder
+
+## GenericNodeEditor 
+nodeEditorLbl=Generic node editor
+genericNodePageTitle=Properties
+childNodesPageTitle=Children
+nodeRightsManagementPageTitle=Rights
+nodeVersionHistoryPageTitle=History
+
+# History 
+versionTreeSectionTitle=Version list
+versionHistorySectionTitle=History
+## Dummy ones 
+testLbl=Internationalizations of messages seems to work properly.
diff --git a/trunk/server/plugins/pom.xml b/trunk/server/plugins/pom.xml
new file mode 100644 (file)
index 0000000..787e634
--- /dev/null
@@ -0,0 +1,40 @@
+<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.11</version>
+               <artifactId>server</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <groupId>org.argeo.commons.server</groupId>
+       <artifactId>plugins</artifactId>
+       <name>Commons Server Eclipse Plugins</name>
+       <packaging>pom</packaging>
+       <modules>
+               <module>org.argeo.jcr.ui.explorer</module>
+       </modules>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                               <configuration>
+                                       <!-- Prevents source jars to contain misleading data -->
+                                       <excludes>
+                                               <exclude>plugin.xml</exclude>
+                                               <exclude>META-INF/MANIFEST.MF</exclude>
+                                       </excludes>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-SymbolicName>${project.artifactId};singleton:=true</Bundle-SymbolicName>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+</project>
diff --git a/trunk/server/pom.xml b/trunk/server/pom.xml
new file mode 100644 (file)
index 0000000..9d20e8e
--- /dev/null
@@ -0,0 +1,19 @@
+<?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">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <version>2.1.11</version>
+               <artifactId>argeo-commons</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>server</artifactId>
+       <name>Commons Server</name>
+       <packaging>pom</packaging>
+       <modules>
+               <module>modules</module>
+               <module>runtime</module>
+               <module>plugins</module>
+               <module>dep</module>
+       </modules>
+</project>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.ads/.classpath b/trunk/server/runtime/org.argeo.server.ads/.classpath
new file mode 100644 (file)
index 0000000..ff41fbb
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/trunk/server/runtime/org.argeo.server.ads/.project b/trunk/server/runtime/org.argeo.server.ads/.project
new file mode 100644 (file)
index 0000000..448ef04
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.ads</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/trunk/server/runtime/org.argeo.server.ads/.settings/org.eclipse.jdt.core.prefs b/trunk/server/runtime/org.argeo.server.ads/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..34d3d60
--- /dev/null
@@ -0,0 +1,67 @@
+#Sun Feb 21 11:17:20 CET 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/server/runtime/org.argeo.server.ads/.settings/org.maven.ide.eclipse.prefs b/trunk/server/runtime/org.argeo.server.ads/.settings/org.maven.ide.eclipse.prefs
new file mode 100644 (file)
index 0000000..8a80a77
--- /dev/null
@@ -0,0 +1,9 @@
+#Mon Nov 23 13:34:22 CET 2009
+activeProfiles=
+eclipse.preferences.version=1
+fullBuildGoals=process-test-resources
+includeModules=false
+resolveWorkspaceProjects=true
+resourceFilterGoals=process-resources resources\:testResources
+skipCompilerPlugin=true
+version=1
diff --git a/trunk/server/runtime/org.argeo.server.ads/build.properties b/trunk/server/runtime/org.argeo.server.ads/build.properties
new file mode 100644 (file)
index 0000000..f883dc4
--- /dev/null
@@ -0,0 +1,2 @@
+additional.bundles = org.apache.directory.server.core
+source.. = src/main/java/
diff --git a/trunk/server/runtime/org.argeo.server.ads/pom.xml b/trunk/server/runtime/org.argeo.server.ads/pom.xml
new file mode 100644 (file)
index 0000000..acdd99b
--- /dev/null
@@ -0,0 +1,88 @@
+<?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.server</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.ads</artifactId>
+       <name>Commons Server Apache Directory Server</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>
+                                                       org.argeo.server.ads.*
+                                               </Export-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Apache DS -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.directory.server.jndi</artifactId>
+                       <exclusions>
+                               <!--
+                                       Workaround for a weird issue where the underlying version from
+                                       slf4j get taken instead of the one we want
+                               -->
+                               <exclusion>
+                                       <groupId>org.slf4j</groupId>
+                                       <artifactId>com.springsource.slf4j.api</artifactId>
+                               </exclusion>
+                       </exclusions>
+               </dependency>
+
+               <!-- Apache Commons -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.io</artifactId>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.core</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.beans</artifactId>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.api</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.ads/src/main/java/org/argeo/server/ads/AdsContainer.java b/trunk/server/runtime/org.argeo.server.ads/src/main/java/org/argeo/server/ads/AdsContainer.java
new file mode 100644 (file)
index 0000000..e95cd8f
--- /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.server.ads;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.naming.directory.InitialDirContext;
+
+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.directory.server.configuration.MutableServerStartupConfiguration;
+import org.apache.directory.server.core.configuration.ShutdownConfiguration;
+import org.apache.directory.server.jndi.ServerContextFactory;
+import org.argeo.ArgeoException;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.core.io.Resource;
+import org.springframework.util.Assert;
+
+/** Wraps an Apache Directory Server instance. */
+@SuppressWarnings("restriction")
+public class AdsContainer implements InitializingBean, DisposableBean {
+       private final static Log log = LogFactory.getLog(AdsContainer.class);
+
+       private MutableServerStartupConfiguration configuration;
+       private Properties environment = null;
+       private File workingDirectory = new File(
+                       System.getProperty("java.io.tmpdir") + File.separator
+                                       + "argeo-apacheDirectoryServer");
+       private Boolean deleteWorkingDirOnExit = false;
+
+       // LDIF
+       private List<Resource> ldifs = new ArrayList<Resource>();
+       private List<String> ignoredLdifAttributes = new ArrayList<String>();
+       /** default is 'demo' */
+       private String ldifPassword = "e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9";
+       private String ldifPasswordAttribute = "userPassword";
+       private File ldifDirectory;
+
+       @SuppressWarnings("unchecked")
+       public void afterPropertiesSet() throws Exception {
+
+               log.info("Starting directory server with id '"
+                               + configuration.getInstanceId() + "' in directory "
+                               + workingDirectory.getAbsolutePath());
+
+               if (deleteWorkingDirOnExit && workingDirectory.exists()) {
+                       log.warn("Found existing directory " + workingDirectory
+                                       + " deleting it...");
+                       FileUtils.deleteDirectory(workingDirectory);
+               }
+               configuration.setWorkingDirectory(workingDirectory);
+               workingDirectory.mkdirs();
+
+               if (ldifDirectory != null)
+                       configuration.setLdifDirectory(ldifDirectory);
+               else
+                       configuration.setLdifDirectory(new File(workingDirectory
+                                       .getAbsolutePath() + File.separator + "ldif"));
+
+               if (ignoredLdifAttributes.size() == 0) {
+                       ignoredLdifAttributes.add("entryUUID");
+                       ignoredLdifAttributes.add("structuralObjectClass");
+                       ignoredLdifAttributes.add("creatorsName");
+                       ignoredLdifAttributes.add("createTimestamp");
+                       ignoredLdifAttributes.add("entryCSN");
+                       ignoredLdifAttributes.add("modifiersName");
+                       ignoredLdifAttributes.add("modifyTimestamp");
+               }
+
+               // Process provided LDIF files
+               if (ldifs.size() > 0)
+                       configuration.getLdifDirectory().mkdirs();
+               for (Resource ldif : ldifs) {
+                       File targetFile = new File(configuration.getLdifDirectory()
+                                       .getAbsolutePath()
+                                       + File.separator
+                                       + ldif.getFilename().replace(':', '_'));
+                       processLdif(ldif, targetFile);
+               }
+
+               Properties env = new Properties();
+               env.setProperty(Context.INITIAL_CONTEXT_FACTORY,
+                               ServerContextFactory.class.getName());
+               Assert.notNull(environment);
+               env.putAll(environment);
+               env.putAll(configuration.toJndiEnvironment());
+
+               try {
+                       new InitialDirContext(env);
+               } catch (NamingException e) {
+                       throw new ArgeoException("Failed to start Apache Directory server",
+                                       e);
+               }
+       }
+
+       /**
+        * Processes an LDIF resource, filtering out attributes that cannot be
+        * imported in ADS and forcing a password.
+        */
+       protected void processLdif(Resource ldif, File targetFile) {
+               BufferedReader reader = null;
+               Writer writer = null;
+               try {
+                       reader = new BufferedReader(new InputStreamReader(
+                                       ldif.getInputStream()));
+                       writer = new FileWriter(targetFile);
+                       String line = null;
+                       lines: while ((line = reader.readLine()) != null) {
+                               // comment and empty lines
+                               if (line.trim().equals("") || line.startsWith("#")) {
+                                       writer.write(line);
+                                       writer.write('\n');
+                                       continue lines;
+                               }
+
+                               String[] tokens = line.split(":");
+                               String attribute = null;
+                               if (tokens != null && tokens.length > 1) {
+                                       attribute = tokens[0].trim();
+                                       if (ignoredLdifAttributes.contains(attribute))
+                                               continue lines;// ignore
+
+                                       if (attribute.equals("bdb_db_open")) {
+                                               log.warn("Ignored OpenLDAP output\n" + line);
+                                               continue lines;
+                                       }
+
+                                       if (ldifPassword != null
+                                                       && attribute.equals(ldifPasswordAttribute)) {
+                                               line = ldifPasswordAttribute + ":: " + ldifPassword;
+                                       }
+
+                                       writer.write(line);
+                                       writer.write('\n');
+                               } else {
+                                       log.warn("Ignored LDIF line\n" + line);
+                               }
+                       }
+                       if (log.isDebugEnabled())
+                               log.debug("Processed " + ldif + " to LDIF directory "
+                                               + configuration.getLdifDirectory());
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot process LDIF " + ldif, e);
+               } finally {
+                       IOUtils.closeQuietly(reader);
+                       IOUtils.closeQuietly(writer);
+               }
+       }
+
+       @SuppressWarnings("unchecked")
+       public void destroy() throws Exception {
+               ShutdownConfiguration shutdown = new ShutdownConfiguration(
+                               configuration.getInstanceId());
+
+               Properties env = new Properties();
+               env.setProperty(Context.INITIAL_CONTEXT_FACTORY,
+                               ServerContextFactory.class.getName());
+               Assert.notNull(environment);
+               env.putAll(environment);
+               env.putAll(shutdown.toJndiEnvironment());
+
+               log.info("Shutting down directory server with id '"
+                               + configuration.getInstanceId() + "'");
+
+               try {
+                       new InitialContext(env);
+               } catch (NamingException e) {
+                       throw new ArgeoException("Failed to stop Apache Directory server",
+                                       e);
+               }
+
+               if (workingDirectory.exists() && deleteWorkingDirOnExit) {
+                       if (log.isDebugEnabled())
+                               log.debug("Delete Apache DS working dir " + workingDirectory);
+                       FileUtils.deleteDirectory(workingDirectory);
+               }
+
+       }
+
+       public void setConfiguration(MutableServerStartupConfiguration configuration) {
+               this.configuration = configuration;
+       }
+
+       public void setWorkingDirectory(File workingDirectory) {
+               this.workingDirectory = workingDirectory;
+       }
+
+       public void setEnvironment(Properties environment) {
+               this.environment = environment;
+       }
+
+       public void setLdifs(List<Resource> ldifs) {
+               this.ldifs = ldifs;
+       }
+
+       public void setLdifDirectory(File ldifDirectory) {
+               this.ldifDirectory = ldifDirectory;
+       }
+
+       public void setDeleteWorkingDirOnExit(Boolean deleteWorkingDirOnExit) {
+               this.deleteWorkingDirOnExit = deleteWorkingDirOnExit;
+       }
+
+       public void setIgnoredLdifAttributes(List<String> ignoredLdifAttributes) {
+               this.ignoredLdifAttributes = ignoredLdifAttributes;
+       }
+
+       public void setLdifPassword(String ldifPassword) {
+               this.ldifPassword = ldifPassword;
+       }
+
+       public void setLdifPasswordAttribute(String ldifPasswordAttribute) {
+               this.ldifPasswordAttribute = ldifPasswordAttribute;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.catalina.start/.classpath b/trunk/server/runtime/org.argeo.server.catalina.start/.classpath
new file mode 100644 (file)
index 0000000..b56882d
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+       <classpathentry kind="src" output="target/classes" path="src/main/java"/>\r
+       <classpathentry kind="src" output="target/classes" path="src/main/resources"/>\r
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>\r
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+       <classpathentry kind="output" path="target/classes"/>\r
+</classpath>\r
diff --git a/trunk/server/runtime/org.argeo.server.catalina.start/.project b/trunk/server/runtime/org.argeo.server.catalina.start/.project
new file mode 100644 (file)
index 0000000..25f3507
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.catalina.start</name>
+       <comment></comment>
+       <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/trunk/server/runtime/org.argeo.server.catalina.start/.settings/org.eclipse.jdt.core.prefs b/trunk/server/runtime/org.argeo.server.catalina.start/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..6d0bce7
--- /dev/null
@@ -0,0 +1,12 @@
+#Fri Apr 23 08:40:00 CEST 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/server/runtime/org.argeo.server.catalina.start/build.properties b/trunk/server/runtime/org.argeo.server.catalina.start/build.properties
new file mode 100644 (file)
index 0000000..2c65f86
--- /dev/null
@@ -0,0 +1,5 @@
+source.. = src/main/java/,\
+           src/main/resources/
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .
diff --git a/trunk/server/runtime/org.argeo.server.catalina.start/pom.xml b/trunk/server/runtime/org.argeo.server.catalina.start/pom.xml
new file mode 100644 (file)
index 0000000..8369b7a
--- /dev/null
@@ -0,0 +1,48 @@
+<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.server</groupId>
+               <version>2.1.11</version>
+               <artifactId>runtime</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.catalina.start</artifactId>
+       <name>Commons Catalina starter</name>
+       <description>Catalina starter hacked from Spring DM sandbox</description>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-Activator>org.argeo.catalina.start.CatalinaActivator</Bundle-Activator>
+                                               <Import-Package>
+                                                       javax.management;version="0.0.0",
+                                                       *
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.eclipse.osgi</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.catalina</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.io</artifactId>
+               </dependency>
+       </dependencies>
+</project>
diff --git a/trunk/server/runtime/org.argeo.server.catalina.start/src/main/java/org/argeo/catalina/start/CatalinaActivator.java b/trunk/server/runtime/org.argeo.server.catalina.start/src/main/java/org/argeo/catalina/start/CatalinaActivator.java
new file mode 100644 (file)
index 0000000..b990c99
--- /dev/null
@@ -0,0 +1,105 @@
+package org.argeo.catalina.start;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Properties;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.BundleContext;
+import org.springframework.osgi.web.tomcat.internal.Activator;
+
+/** Starts Catalina (hacked from Spring OSGi 1.0) */
+public class CatalinaActivator extends Activator {
+       private final static Log log = LogFactory.getLog(CatalinaActivator.class);
+
+       private final static String ARGEO_OSGI_DATA_DIR = "argeo.osgi.data.dir";
+       /** System properties used to override Tomcat XML config URL */
+       public final static String ARGEO_SERVER_TOMCAT_CONFIG = "argeo.server.tomcat.config";
+
+       public void start(BundleContext context) throws Exception {
+               if (!System.getProperties().containsKey(ARGEO_OSGI_DATA_DIR)) {
+                       String osgiInstanceArea = System.getProperty("osgi.instance.area");
+                       String osgiInstanceAreaDefault = System
+                                       .getProperty("osgi.instance.area.default");
+                       String tempDir = System.getProperty("java.io.tmpdir");
+
+                       File dataDir = null;
+                       if (osgiInstanceArea != null) {
+                               // within OSGi with -data specified
+                               osgiInstanceArea = removeFilePrefix(osgiInstanceArea);
+                               dataDir = new File(osgiInstanceArea);
+                       } else if (osgiInstanceAreaDefault != null) {
+                               // within OSGi without -data specified
+                               osgiInstanceAreaDefault = removeFilePrefix(osgiInstanceAreaDefault);
+                               dataDir = new File(osgiInstanceAreaDefault);
+                       } else {// outside OSGi
+                               dataDir = new File(tempDir + File.separator + "osgiData");
+                       }
+
+                       System.setProperty(ARGEO_OSGI_DATA_DIR, dataDir.getAbsolutePath());
+               }
+
+               // Load config properties and put them in system properties so that they
+               // can be used in tomcat conf
+               Properties confProps = new Properties();
+               URL propsUrl = context.getBundle().getResource("tomcat.properties");
+               if (propsUrl != null) {
+                       InputStream in = null;
+                       try {
+                               in = propsUrl.openStream();
+                               confProps.load(in);
+                       } catch (Exception e) {
+                               throw new RuntimeException("Cannot read catalina properties.",
+                                               e);
+                       } finally {
+                               IOUtils.closeQuietly(in);
+                       }
+
+                       for (Object key : confProps.keySet()) {
+                               // System properties have priority
+                               if (!System.getProperties().containsKey(key)) {
+                                       System.setProperty(key.toString(),
+                                                       confProps.getProperty(key.toString()));
+                               }
+                       }
+               }
+
+               // calling Catalina.setCatalinaHome(String) or
+               // Catalina.setCatalinaBase(String) does the same
+               if (System.getProperty("catalina.home") == null)
+                       System.setProperty("catalina.home",
+                                       System.getProperty(ARGEO_OSGI_DATA_DIR) + "/tomcat");
+               if (System.getProperty("catalina.base") == null)
+                       System.setProperty("catalina.base",
+                                       System.getProperty(ARGEO_OSGI_DATA_DIR) + "/tomcat");
+
+               // Make sure directories are created
+               File catalinaDir = new File(System.getProperty("catalina.home"));
+               if (!catalinaDir.exists()) {
+                       catalinaDir.mkdirs();
+                       if (log.isDebugEnabled())
+                               log.debug("Created Tomcat directory " + catalinaDir);
+               }
+
+               // Call Spring starter
+               super.start(context);
+       }
+
+       @Override
+       public void stop(BundleContext context) throws Exception {
+               super.stop(context);
+       }
+
+       protected 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;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.catalina.start/src/main/java/org/springframework/osgi/web/tomcat/internal/Activator.java b/trunk/server/runtime/org.argeo.server.catalina.start/src/main/java/org/springframework/osgi/web/tomcat/internal/Activator.java
new file mode 100644 (file)
index 0000000..17ed5e9
--- /dev/null
@@ -0,0 +1,278 @@
+/*\r
+ * Copyright 2006-2008 the original author or authors.\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.springframework.osgi.web.tomcat.internal;\r
+\r
+import java.io.File;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.net.MalformedURLException;\r
+import java.net.URL;\r
+import java.net.URLConnection;\r
+import java.net.URLStreamHandler;\r
+import java.util.Properties;\r
+\r
+import javax.management.MBeanRegistration;\r
+\r
+import org.apache.catalina.Lifecycle;\r
+import org.apache.catalina.Server;\r
+import org.apache.catalina.Service;\r
+import org.apache.catalina.connector.Connector;\r
+import org.apache.catalina.core.StandardService;\r
+import org.apache.catalina.util.ServerInfo;\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+import org.apache.naming.resources.DirContextURLStreamHandler;\r
+import org.argeo.catalina.start.CatalinaActivator;\r
+import org.osgi.framework.Bundle;\r
+import org.osgi.framework.BundleActivator;\r
+import org.osgi.framework.BundleContext;\r
+import org.osgi.framework.Constants;\r
+import org.osgi.framework.ServiceRegistration;\r
+import org.osgi.service.url.AbstractURLStreamHandlerService;\r
+import org.osgi.service.url.URLConstants;\r
+import org.osgi.service.url.URLStreamHandlerService;\r
+\r
+/**\r
+ * Simple activator for starting Apache Tomcat Catalina container inside OSGi\r
+ * using Tomcat's XML configuration files.\r
+ * \r
+ * <p/>\r
+ * This activator looks initially for a <code>conf/server.xml</code> file\r
+ * falling back to <code>conf/default-server.xml</code>. This allows the default\r
+ * configuration to be tweaked through fragments for example.\r
+ * \r
+ * @author Costin Leau\r
+ */\r
+public class Activator implements BundleActivator {\r
+\r
+       /** logger */\r
+       private static final Log log = LogFactory.getLog(Activator.class);\r
+\r
+       /** default XML configuration */\r
+       private static final String DEFAULT_XML_CONF_LOCATION = "conf/default-server.xml";\r
+\r
+       /** user-configurable XML configuration */\r
+       private static final String XML_CONF_LOCATION = "conf/server.xml";\r
+\r
+       private BundleContext bundleContext;\r
+\r
+       private StandardService server;\r
+\r
+       private ServiceRegistration registration, urlRegistration;\r
+\r
+       private Thread startupThread;\r
+\r
+       public void start(BundleContext context) throws Exception {\r
+               this.bundleContext = context;\r
+               // do the initialization on a different thread\r
+               // so the activator finishes fast\r
+               startupThread = new Thread(new Runnable() {\r
+\r
+                       public void run() {\r
+                               log.info("Starting " + ServerInfo.getServerInfo() + " ...");\r
+\r
+                               // default startup procedure\r
+                               ClassLoader cl = Activator.class.getClassLoader();\r
+                               Thread current = Thread.currentThread();\r
+                               ClassLoader old = current.getContextClassLoader();\r
+\r
+                               try {\r
+                                       current.setContextClassLoader(cl);\r
+\r
+                                       server = createCatalinaServer(bundleContext.getBundle());\r
+\r
+                                       server.start();\r
+\r
+                                       Connector[] connectors = server.findConnectors();\r
+                                       for (int i = 0; i < connectors.length; i++) {\r
+                                               Connector conn = connectors[i];\r
+                                               log.info("Succesfully started "\r
+                                                               + ServerInfo.getServerInfo() + " @ "\r
+                                                               + conn.getDomain() + ":" + conn.getPort());\r
+                                       }\r
+\r
+                                       // register URL service\r
+                                       urlRegistration = registerTomcatJNDIUrlService();\r
+                                       // publish server as an OSGi service\r
+                                       registration = publishServerAsAService(server);\r
+                                       log.info("Published " + ServerInfo.getServerInfo()\r
+                                                       + " as an OSGi service");\r
+                               } catch (Exception ex) {\r
+                                       String msg = "Cannot start " + ServerInfo.getServerInfo();\r
+                                       log.error(msg, ex);\r
+                                       throw new RuntimeException(msg, ex);\r
+                               } finally {\r
+                                       current.setContextClassLoader(old);\r
+                               }\r
+                       }\r
+               }, "Tomcat Catalina Start Thread");\r
+\r
+               startupThread.start();\r
+       }\r
+\r
+       public void stop(BundleContext context) throws Exception {\r
+               // unpublish service first\r
+               registration.unregister();\r
+               urlRegistration.unregister();\r
+\r
+               log.info("Unpublished  " + ServerInfo.getServerInfo() + " OSGi service");\r
+\r
+               // default startup procedure\r
+               ClassLoader cl = Activator.class.getClassLoader();\r
+               Thread current = Thread.currentThread();\r
+               ClassLoader old = current.getContextClassLoader();\r
+\r
+               try {\r
+                       current.setContextClassLoader(cl);\r
+                       // reset CCL\r
+                       // current.setContextClassLoader(null);\r
+                       log.info("Stopping " + ServerInfo.getServerInfo() + " ...");\r
+                       server.stop();\r
+                       log.info("Succesfully stopped " + ServerInfo.getServerInfo());\r
+               } catch (Exception ex) {\r
+                       log.error("Cannot stop " + ServerInfo.getServerInfo(), ex);\r
+                       throw ex;\r
+               } finally {\r
+                       current.setContextClassLoader(old);\r
+               }\r
+       }\r
+\r
+       private StandardService createCatalinaServer(Bundle bundle)\r
+                       throws Exception {\r
+               URL xmlConfiguration = null;\r
+\r
+               if (System.getProperty(CatalinaActivator.ARGEO_SERVER_TOMCAT_CONFIG) != null) {\r
+                       String customConfig = System\r
+                                       .getProperty(CatalinaActivator.ARGEO_SERVER_TOMCAT_CONFIG);\r
+                       try {\r
+                               xmlConfiguration = new URL(customConfig);\r
+                       } catch (MalformedURLException e) {\r
+                               // within this bundle\r
+                               // typically 'default-server-ssl.xml'\r
+                               xmlConfiguration = bundle.getResource(customConfig);\r
+                       }\r
+               } else {\r
+                       // fragment\r
+                       xmlConfiguration = bundle.getResource(XML_CONF_LOCATION);\r
+               }\r
+\r
+               if (xmlConfiguration != null) {\r
+                       log.info("Using custom XML configuration " + xmlConfiguration);\r
+               } else {\r
+                       xmlConfiguration = bundle.getResource(DEFAULT_XML_CONF_LOCATION);\r
+                       if (xmlConfiguration == null)\r
+                               log.error("No XML configuration found; bailing out...");\r
+                       else\r
+                               log.info("Using default XML configuration " + xmlConfiguration);\r
+               }\r
+\r
+               return createServerFromXML(xmlConfiguration);\r
+       }\r
+\r
+       private StandardService createServerFromXML(URL xmlConfiguration)\r
+                       throws IOException {\r
+               OsgiCatalina catalina = new OsgiCatalina();\r
+               catalina.setAwait(false);\r
+               catalina.setUseShutdownHook(false);\r
+               catalina.setName("Catalina");\r
+               catalina.setParentClassLoader(Thread.currentThread()\r
+                               .getContextClassLoader());\r
+\r
+               // copy the URL file to a local temporary file (since Catalina doesn't\r
+               // use URL unfortunately)\r
+               File configTempFile = File.createTempFile("dm.catalina", ".cfg.xml");\r
+               configTempFile.deleteOnExit();\r
+\r
+               // copy URL to temporary file\r
+               copyURLToFile(xmlConfiguration.openStream(), new FileOutputStream(\r
+                               configTempFile));\r
+               log.debug("Copied configuration " + xmlConfiguration\r
+                               + " to temporary file " + configTempFile);\r
+\r
+               catalina.setConfigFile(configTempFile.getAbsolutePath());\r
+\r
+               catalina.load();\r
+\r
+               Server server = catalina.getServer();\r
+\r
+               return (StandardService) server.findServices()[0];\r
+       }\r
+\r
+       private void copyURLToFile(InputStream inStream, FileOutputStream outStream) {\r
+\r
+               int bytesRead;\r
+               byte[] buf = new byte[4096];\r
+               try {\r
+                       while ((bytesRead = inStream.read(buf)) >= 0) {\r
+                               outStream.write(buf, 0, bytesRead);\r
+                       }\r
+               } catch (IOException ex) {\r
+                       throw (RuntimeException) new IllegalStateException(\r
+                                       "Cannot copy URL to file").initCause(ex);\r
+               } finally {\r
+                       try {\r
+                               inStream.close();\r
+                       } catch (IOException ignore) {\r
+                       }\r
+                       try {\r
+                               outStream.close();\r
+                       } catch (IOException ignore) {\r
+                       }\r
+               }\r
+       }\r
+\r
+       private ServiceRegistration publishServerAsAService(StandardService server) {\r
+               Properties props = new Properties();\r
+               // put some extra properties to easily identify the service\r
+               props.put(Constants.SERVICE_VENDOR, "Spring Dynamic Modules");\r
+               props.put(Constants.SERVICE_DESCRIPTION, ServerInfo.getServerInfo());\r
+               props.put(Constants.BUNDLE_VERSION, ServerInfo.getServerNumber());\r
+               props.put(Constants.BUNDLE_NAME, bundleContext.getBundle()\r
+                               .getSymbolicName());\r
+\r
+               // spring-dm specific property\r
+               props.put("org.springframework.osgi.bean.name", "tomcat-server");\r
+\r
+               // publish just the interfaces and the major classes\r
+               // (server/handlerWrapper)\r
+               String[] classes = new String[] { StandardService.class.getName(),\r
+                               Service.class.getName(), MBeanRegistration.class.getName(),\r
+                               Lifecycle.class.getName() };\r
+\r
+               return bundleContext.registerService(classes, server, props);\r
+       }\r
+\r
+       private ServiceRegistration registerTomcatJNDIUrlService() {\r
+               Properties properties = new Properties();\r
+               properties.put(URLConstants.URL_HANDLER_PROTOCOL, "jndi");\r
+               final URLStreamHandler handler = new DirContextURLStreamHandler();\r
+\r
+               return bundleContext.registerService(\r
+                               URLStreamHandlerService.class.getName(),\r
+                               new AbstractURLStreamHandlerService() {\r
+\r
+                                       private final static String EMPTY_STRING = "";\r
+\r
+                                       public URLConnection openConnection(URL u)\r
+                                                       throws IOException {\r
+                                               return new URL(u, EMPTY_STRING, handler)\r
+                                                               .openConnection();\r
+                                       }\r
+                               }, properties);\r
+       }\r
+}
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.catalina.start/src/main/java/org/springframework/osgi/web/tomcat/internal/OsgiCatalina.java b/trunk/server/runtime/org.argeo.server.catalina.start/src/main/java/org/springframework/osgi/web/tomcat/internal/OsgiCatalina.java
new file mode 100644 (file)
index 0000000..f7903ff
--- /dev/null
@@ -0,0 +1,34 @@
+/*\r
+ * Copyright 2006-2008 the original author or authors.\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.springframework.osgi.web.tomcat.internal;\r
+\r
+import org.apache.catalina.Server;\r
+import org.apache.catalina.startup.Catalina;\r
+\r
+/**\r
+ * OSGi extension of Catalina class used for easy access to the underlying\r
+ * configuration.\r
+ * \r
+ * @author Costin Leau\r
+ * \r
+ */\r
+public class OsgiCatalina extends Catalina {\r
+\r
+       public Server getServer() {\r
+               return server;\r
+       }\r
+}\r
diff --git a/trunk/server/runtime/org.argeo.server.catalina.start/src/main/resources/conf/default-server-ssl.xml b/trunk/server/runtime/org.argeo.server.catalina.start/src/main/resources/conf/default-server-ssl.xml
new file mode 100644 (file)
index 0000000..0417b7c
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version='1.0' encoding='utf-8'?>
+<Server port="-1" shutdown="SHUTDOWN">
+       <!--APR library loader. Documentation at /docs/apr.html -->
+       <!-- <Listener className="org.apache.catalina.core.AprLifecycleListener" 
+               SSLEngine="on" /> -->
+       <!-- Initialize Jasper prior to webapps are loaded. -->
+       <Listener className="org.apache.catalina.core.JasperListener" />
+       <!-- JMX -->
+       <!-- <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" 
+               /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" 
+               /> -->
+
+       <Service name="Catalina">
+               <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
+                       maxThreads="150" minSpareThreads="4" />
+
+               <!-- HTTP -->
+               <Connector executor="tomcatThreadPool" port="${argeo.server.port.http}"
+                       protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="${argeo.server.port.https}"
+                       secure="${argeo.server.http.secure}" proxyName="${argeo.server.http.proxyName}"
+                       proxyPort="${argeo.server.http.proxyPort}" />
+               <!-- HTTPS -->
+               <Connector port="${argeo.server.port.https}" protocol="HTTP/1.1"
+                       SSLEnabled="true" scheme="https" secure="true" sslProtocol="TLS"
+                       keystoreFile="${argeo.server.keystoreFile}" keystoreType="JKS"
+                       keystorePass="${argeo.server.keystorePass}" truststoreFile="${argeo.server.truststoreFile}"
+                       truststoreType="JKS" truststorePass="${argeo.server.truststorePass}"
+                       clientAuth="${argeo.server.https.clientAuth}" />
+                        
+               <!-- AJP (for proxying with httpd) -->
+               <Connector port="${argeo.server.port.ajp}" protocol="AJP/1.3"
+                       redirectPort="${argeo.server.port.https}" />
+
+               <Engine name="Catalina" defaultHost="localhost">
+                       <Host name="localhost" appBase="webapps" unpackWARs="true"
+                               autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false"
+                               workDir="work">
+                       </Host>
+               </Engine>
+       </Service>
+</Server>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.catalina.start/src/main/resources/conf/default-server.xml b/trunk/server/runtime/org.argeo.server.catalina.start/src/main/resources/conf/default-server.xml
new file mode 100644 (file)
index 0000000..a558e13
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version='1.0' encoding='utf-8'?>
+<Server port="-1" shutdown="SHUTDOWN">
+       <!--APR library loader. Documentation at /docs/apr.html -->
+       <!-- <Listener className="org.apache.catalina.core.AprLifecycleListener" 
+               SSLEngine="on" /> -->
+       <!-- Initialize Jasper prior to webapps are loaded. -->
+       <Listener className="org.apache.catalina.core.JasperListener" />
+       <!-- JMX -->
+       <!-- <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" 
+               /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" 
+               /> -->
+
+       <Service name="Catalina">
+               <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
+                       maxThreads="150" minSpareThreads="4" />
+
+               <!-- HTTP -->
+               <Connector executor="tomcatThreadPool" port="${argeo.server.port.http}"
+                       protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="${argeo.server.port.https}"
+                       secure="${argeo.server.http.secure}" proxyName="${argeo.server.http.proxyName}"
+                       proxyPort="${argeo.server.http.proxyPort}" />
+               <!-- HTTPS
+               <Connector port="${argeo.server.port.https}" protocol="HTTP/1.1"
+                       SSLEnabled="true" scheme="https" secure="true" sslProtocol="TLS"
+                       keystoreFile="${argeo.server.keystoreFile}" keystoreType="JKS"
+                       keystorePass="${argeo.server.keystorePass}" truststoreFile="${argeo.server.truststoreFile}"
+                       truststoreType="JKS" truststorePass="${argeo.server.truststorePass}"
+                       clientAuth="${argeo.server.https.clientAuth}" />
+                        -->
+               <!-- AJP (for proxying with httpd) -->
+               <Connector port="${argeo.server.port.ajp}" protocol="AJP/1.3"
+                       redirectPort="${argeo.server.port.https}" />
+
+               <Engine name="Catalina" defaultHost="localhost">
+                       <Host name="localhost" appBase="webapps" unpackWARs="true"
+                               autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false"
+                               workDir="work">
+                       </Host>
+               </Engine>
+       </Service>
+</Server>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.catalina.start/tomcat.properties b/trunk/server/runtime/org.argeo.server.catalina.start/tomcat.properties
new file mode 100644 (file)
index 0000000..5e6a5f1
--- /dev/null
@@ -0,0 +1,14 @@
+argeo.server.port.http=7070
+argeo.server.port.https=7073
+argeo.server.port.ajp=7079
+
+# Used only when SSL is activated (uncommented in server.xml)
+argeo.server.keystoreFile=../../../../ssl/server.ks
+argeo.server.keystorePass=changeit
+argeo.server.truststoreFile=../../../../ssl/server.ts
+argeo.server.truststorePass=changeit
+argeo.server.https.clientAuth=want
+
+argeo.server.http.proxyName=
+argeo.server.http.proxyPort=
+argeo.server.http.secure=false
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.core/.classpath b/trunk/server/runtime/org.argeo.server.core/.classpath
new file mode 100644 (file)
index 0000000..3bf3ade
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/server/runtime/org.argeo.server.core/.project b/trunk/server/runtime/org.argeo.server.core/.project
new file mode 100644 (file)
index 0000000..b28214b
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.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/trunk/server/runtime/org.argeo.server.core/build.properties b/trunk/server/runtime/org.argeo.server.core/build.properties
new file mode 100644 (file)
index 0000000..92847ec
--- /dev/null
@@ -0,0 +1,7 @@
+source.. = src/main/java/
+additional.bundles = slf4j.api,\
+                     slf4j.org.apache.commons.logging,\
+                     slf4j.log4j,\
+                     org.apache.log4j,\
+                     com.jcraft.jsch
+                     
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.core/pom.xml b/trunk/server/runtime/org.argeo.server.core/pom.xml
new file mode 100644 (file)
index 0000000..3a189ea
--- /dev/null
@@ -0,0 +1,114 @@
+<?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.server</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.core</artifactId>
+       <name>Commons Server Core</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>
+                                                       org.argeo.server.*
+                                               </Export-Package>
+                                               <Import-Package>
+                                                       javax.servlet,
+                                                       org.springframework.web.context,
+                                                       *
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Apache Commons -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.io</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.vfs</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.exec</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>com.jcraft.jsch</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.joda.time</artifactId>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.web</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.beans</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.context</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.core</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.web.servlet</artifactId>
+               </dependency>
+
+               <!-- J2EE -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.servlet</artifactId>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.fileupload</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/BooleanAnswer.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/BooleanAnswer.java
new file mode 100644 (file)
index 0000000..2b7ffb2
--- /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.server;
+
+
+/** Answer to an execution of a remote service which performed changes. */
+public class BooleanAnswer {
+       private Boolean value = Boolean.TRUE;
+
+       /** Canonical constructor */
+       public BooleanAnswer(Boolean status) {
+               this.value = status;
+       }
+
+       /** Empty constructor */
+       public BooleanAnswer() {
+       }
+
+       public Boolean getValue() {
+               return value;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/Deserializer.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/Deserializer.java
new file mode 100644 (file)
index 0000000..4d7faf9
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+import java.io.Reader;
+
+public interface Deserializer {
+       public <T> T deserialize(Reader reader, Class<T> clss);
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/DeserializingEditor.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/DeserializingEditor.java
new file mode 100644 (file)
index 0000000..915b71c
--- /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.server;
+
+import java.beans.PropertyEditorSupport;
+import java.io.StringReader;
+
+import org.apache.commons.io.IOUtils;
+
+public class DeserializingEditor extends PropertyEditorSupport {
+       private final Deserializer deserializer;
+       private final Class<?> targetClass;
+
+       public DeserializingEditor(Deserializer deserializer, Class<?> targetClass) {
+               super();
+               this.deserializer = deserializer;
+               this.targetClass = targetClass;
+       }
+
+       @Override
+       public void setAsText(String text) throws IllegalArgumentException {
+               StringReader reader = new StringReader(text);
+               try {
+                       setValue(deserializer.deserialize(reader, targetClass));
+               } finally {
+                       IOUtils.closeQuietly(reader);
+               }
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/PrimitiveAnswer.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/PrimitiveAnswer.java
new file mode 100644 (file)
index 0000000..c69ffed
--- /dev/null
@@ -0,0 +1,34 @@
+/*\r
+ * Copyright (C) 2007-2012 Argeo GmbH\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *         http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.argeo.server;\r
+\r
+/**\r
+ * Answer to a request to a remote service that sends back only one primitive\r
+ */\r
+public class PrimitiveAnswer {\r
+\r
+       private Object primitive;\r
+\r
+       /** Canonical constructor */\r
+       public PrimitiveAnswer(Object primitive) {\r
+               this.primitive = primitive;\r
+       }\r
+\r
+       public Object getValue() {\r
+               return primitive;\r
+       }\r
+\r
+}\r
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/Serializer.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/Serializer.java
new file mode 100644 (file)
index 0000000..890b17c
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+import java.io.Writer;
+
+public interface Serializer {
+       /** Will be removed soon. Use {@link #serialize(Object, Writer)} instead. */
+       @Deprecated
+       public void serialize(Writer writer, Object obj);
+
+       public void serialize(Object obj, Writer writer);
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/ServerAnswer.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/ServerAnswer.java
new file mode 100644 (file)
index 0000000..2f9cbee
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.ArgeoException;
+
+/** Answer to an execution of a remote service which performed changes. */
+public class ServerAnswer {
+       public final static String OK = "OK";
+       public final static String ERROR = "ERROR";
+
+       private String status = OK;
+       private String message = "";
+
+       // TODO: add an exception field
+
+       /** Canonical constructor */
+       public ServerAnswer(String status, String message) {
+               setStatus(status);
+               if (message == null)
+                       throw new ArgeoException("Message cannot be null");
+               this.message = message;
+       }
+
+       /** Empty constructor */
+       public ServerAnswer() {
+       }
+
+       public String getStatus() {
+               return status;
+       }
+
+       public void setStatus(String status) {
+               if (status == null || (!status.equals(OK) && !status.equals(ERROR)))
+                       throw new ArgeoException("Bad status format: " + status);
+               this.status = status;
+       }
+
+       public String getMessage() {
+               return message;
+       }
+
+       public void setMessage(String message) {
+               this.message = message;
+       }
+
+       public Boolean isOk() {
+               return status.equals(OK);
+       }
+
+       public Boolean isError() {
+               return status.equals(ERROR);
+       }
+
+       public static ServerAnswer error(String message) {
+               return new ServerAnswer(ERROR, message);
+       }
+
+       public static ServerAnswer error(Throwable e) {
+               StringWriter writer = new StringWriter();
+               try {
+                       e.printStackTrace(new PrintWriter(writer));
+                       return new ServerAnswer(ERROR, writer.toString());
+               } finally {
+                       IOUtils.closeQuietly(writer);
+               }
+       }
+
+       public static ServerAnswer ok(String message) {
+               return new ServerAnswer(OK, message);
+       }
+
+       @Override
+       public String toString() {
+               return "ServerAnswer{status:" + status + ", message:" + message + "}";
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/ServerSerializer.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/ServerSerializer.java
new file mode 100644 (file)
index 0000000..cdb1632
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public interface ServerSerializer {
+       public void serialize(Object obj, HttpServletRequest request,
+                       HttpServletResponse response);
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/AbstractAtomicBackup.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/AbstractAtomicBackup.java
new file mode 100644 (file)
index 0000000..53972e5
--- /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.server.backup;
+
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemManager;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.provider.sftp.SftpFileSystemConfigBuilder;
+import org.argeo.ArgeoException;
+
+/**
+ * Simplify atomic backups implementation, especially by managing VFS.
+ */
+public abstract class AbstractAtomicBackup implements AtomicBackup {
+       private String name;
+       private String compression = "bz2";
+
+       protected abstract void writeBackup(FileObject targetFo);
+
+       public AbstractAtomicBackup() {
+       }
+
+       public AbstractAtomicBackup(String name) {
+               this.name = name;
+       }
+
+       public void init() {
+               if (name == null)
+                       throw new ArgeoException("Atomic backup name must be set");
+       }
+
+       public void destroy() {
+
+       }
+
+       @Override
+       public String backup(FileSystemManager fileSystemManager,
+                       String backupsBase, BackupContext backupContext,
+                       FileSystemOptions opts) {
+               if (name == null)
+                       throw new ArgeoException("Atomic backup name must be set");
+
+               FileObject targetFo = null;
+               try {
+                       if (backupsBase.startsWith("sftp:"))
+                               SftpFileSystemConfigBuilder.getInstance()
+                                               .setStrictHostKeyChecking(opts, "no");
+                       if (compression == null || compression.equals("none"))
+                               targetFo = fileSystemManager.resolveFile(backupsBase + '/'
+                                               + backupContext.getRelativeFolder() + '/' + name, opts);
+                       else if (compression.equals("bz2"))
+                               targetFo = fileSystemManager.resolveFile("bz2:" + backupsBase
+                                               + '/' + backupContext.getRelativeFolder() + '/' + name
+                                               + ".bz2" + "!" + name, opts);
+                       else if (compression.equals("gz"))
+                               targetFo = fileSystemManager.resolveFile("gz:" + backupsBase
+                                               + '/' + backupContext.getRelativeFolder() + '/' + name
+                                               + ".gz" + "!" + name, opts);
+                       else
+                               throw new ArgeoException("Unsupported compression "
+                                               + compression);
+
+                       writeBackup(targetFo);
+
+                       return targetFo.toString();
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot backup " + name + " to "
+                                       + targetFo, e);
+               } finally {
+                       BackupUtils.closeFOQuietly(targetFo);
+               }
+       }
+
+       public void setName(String name) {
+               this.name = name;
+       }
+
+       public String getName() {
+               return name;
+       }
+
+       public void setCompression(String compression) {
+               this.compression = compression;
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/AtomicBackup.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/AtomicBackup.java
new file mode 100644 (file)
index 0000000..3ebe4b5
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.backup;
+
+import org.apache.commons.vfs.FileSystemManager;
+import org.apache.commons.vfs.FileSystemOptions;
+
+/** Performs the backup of a single component, typically a database dump */
+public interface AtomicBackup {
+       /** Name identifiying this backup */
+       public String getName();
+
+       /**
+        * Retrieves the data of the component in a format that allows to restore
+        * the component
+        * 
+        * @param backupContext
+        *            the context of this backup
+        * @return the VFS URI of the generated file or directory
+        */
+       public String backup(FileSystemManager fileSystemManager,
+                       String backupsBase, BackupContext backupContext,
+                       FileSystemOptions opts);
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/BackupContext.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/BackupContext.java
new file mode 100644 (file)
index 0000000..5f6442f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.backup;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+/**
+ * Transient information of a given backup, centralizing common information such
+ * as timestamp and location.
+ */
+public interface BackupContext {
+       /** Backup date */
+       public Date getTimestamp();
+
+       /** Formatted backup date */
+       public String getTimestampAsString();
+
+       /** System name */
+       public String getSystemName();
+
+       /** Local base */
+       public String getRelativeFolder();
+
+       /** Date format */
+       public DateFormat getDateFormat();
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/BackupFileSystemManager.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/BackupFileSystemManager.java
new file mode 100644 (file)
index 0000000..9a0549c
--- /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.server.backup;
+
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.impl.DefaultFileSystemManager;
+import org.apache.commons.vfs.provider.bzip2.Bzip2FileProvider;
+import org.apache.commons.vfs.provider.ftp.FtpFileProvider;
+import org.apache.commons.vfs.provider.gzip.GzipFileProvider;
+import org.apache.commons.vfs.provider.local.DefaultLocalFileProvider;
+import org.apache.commons.vfs.provider.ram.RamFileProvider;
+import org.apache.commons.vfs.provider.sftp.SftpFileProvider;
+import org.apache.commons.vfs.provider.url.UrlFileProvider;
+import org.argeo.ArgeoException;
+
+/**
+ * Programatically configured VFS file system manager which can be declared as a
+ * bean and associated with a life cycle (methods
+ * {@link DefaultFileSystemManager#init()} and
+ * {@link DefaultFileSystemManager#closet()}). Supports bz2, file, ram, gzip,
+ * ftp, sftp
+ */
+public class BackupFileSystemManager extends DefaultFileSystemManager {
+
+       public BackupFileSystemManager() {
+               super();
+               try {
+                       addProvider("file", new DefaultLocalFileProvider());
+                       addProvider("bz2", new Bzip2FileProvider());
+                       addProvider("ftp", new FtpFileProvider());
+                       addProvider("sftp", new SftpFileProvider());
+                       addProvider("gzip", new GzipFileProvider());
+                       addProvider("ram", new RamFileProvider());
+                       setDefaultProvider(new UrlFileProvider());
+               } catch (FileSystemException e) {
+                       throw new ArgeoException("Cannot configure backup file provider", e);
+               }
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/BackupPurge.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/BackupPurge.java
new file mode 100644 (file)
index 0000000..37eecd6
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.backup;
+
+import java.text.DateFormat;
+
+import org.apache.commons.vfs.FileSystemManager;
+import org.apache.commons.vfs.FileSystemOptions;
+
+/** Purges previous backups */
+public interface BackupPurge {
+       /**
+        * Purge the backups identified by these arguments. Although these are the
+        * same fields as a {@link BackupContext} we don't pass it as argument since
+        * we want to use this interface to purge remote backups as well (that is,
+        * with a different base), or outside the scope of a running backup.
+        */
+       public void purge(FileSystemManager fileSystemManager, String base,
+                       String name, DateFormat dateFormat, FileSystemOptions opts);
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/BackupUtils.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/BackupUtils.java
new file mode 100644 (file)
index 0000000..e949b0c
--- /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.server.backup;
+
+import org.apache.commons.vfs.FileObject;
+
+/** Backup utilities */
+public class BackupUtils {
+       /** Close a file object quietly even if it is null or throws an exception. */
+       public static void closeFOQuietly(FileObject fo) {
+               if (fo != null) {
+                       try {
+                               fo.close();
+                       } catch (Exception e) {
+                               // silent
+                       }
+               }
+       }
+       
+       /** Prevents instantiation */
+       private BackupUtils() {
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/MySqlBackup.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/MySqlBackup.java
new file mode 100644 (file)
index 0000000..8d33771
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.backup;
+
+import org.apache.commons.vfs.FileObject;
+
+/** Backups a MySQL database using mysqldump. */
+public class MySqlBackup extends OsCallBackup {
+       private String mysqldumpLocation = "/usr/bin/mysqldump";
+
+       private String dbUser;
+       private String dbPassword;
+       private String dbName;
+
+       public MySqlBackup() {
+       }
+
+       public MySqlBackup(String dbUser, String dbPassword, String dbName) {
+               this.dbUser = dbUser;
+               this.dbPassword = dbPassword;
+               this.dbName = dbName;
+               init();
+       }
+
+       @Override
+       public void init() {
+               if (getName() == null)
+                       setName(dbName + ".mysql");
+               super.init();
+       }
+
+       @Override
+       public void writeBackup(FileObject targetFo) {
+               if (getCommand() == null)
+                       setCommand(mysqldumpLocation
+                                       + " --lock-tables --add-locks --add-drop-table"
+                                       + " -u ${dbUser} --password=${dbPassword} --databases ${dbName}");
+               getVariables().put("dbUser", dbUser);
+               getVariables().put("dbPassword", dbPassword);
+               getVariables().put("dbName", dbName);
+
+               super.writeBackup(targetFo);
+       }
+
+       public void setDbUser(String dbUser) {
+               this.dbUser = dbUser;
+       }
+
+       public void setDbPassword(String dbPassword) {
+               this.dbPassword = dbPassword;
+       }
+
+       public void setDbName(String dbName) {
+               this.dbName = dbName;
+       }
+
+       public void setMysqldumpLocation(String mysqldumpLocation) {
+               this.mysqldumpLocation = mysqldumpLocation;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/OpenLdapBackup.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/OpenLdapBackup.java
new file mode 100644 (file)
index 0000000..eb5ce2d
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.backup;
+
+import org.apache.commons.vfs.FileObject;
+import org.argeo.ArgeoException;
+
+/** Backups an OpenLDAP server using slapcat */
+public class OpenLdapBackup extends OsCallBackup {
+       private String slapcatLocation = "/usr/sbin/slapcat";
+       private String slapdConfLocation = "/etc/openldap/slapd.conf";
+       private String baseDn;
+
+       public OpenLdapBackup() {
+               super();
+       }
+
+       public OpenLdapBackup(String baseDn) {
+               super();
+               this.baseDn = baseDn;
+       }
+
+       @Override
+       public void writeBackup(FileObject targetFo) {
+               if (baseDn == null)
+                       throw new ArgeoException("Base DN must be set");
+
+               if (getCommand() == null)
+                       setCommand(slapcatLocation
+                                       + " -f ${slapdConfLocation} -b '${baseDn}'");
+               getVariables().put("slapdConfLocation", slapdConfLocation);
+               getVariables().put("baseDn", baseDn);
+
+               super.writeBackup(targetFo);
+       }
+
+       public void setSlapcatLocation(String slapcatLocation) {
+               this.slapcatLocation = slapcatLocation;
+       }
+
+       public void setSlapdConfLocation(String slapdConfLocation) {
+               this.slapdConfLocation = slapdConfLocation;
+       }
+
+       public void setBaseDn(String baseDn) {
+               this.baseDn = baseDn;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/OsCallBackup.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/OsCallBackup.java
new file mode 100644 (file)
index 0000000..816157c
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * 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.backup;
+
+import java.io.ByteArrayOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.DefaultExecutor;
+import org.apache.commons.exec.ExecuteException;
+import org.apache.commons.exec.ExecuteStreamHandler;
+import org.apache.commons.exec.Executor;
+import org.apache.commons.exec.PumpStreamHandler;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.vfs.FileContent;
+import org.apache.commons.vfs.FileObject;
+import org.argeo.ArgeoException;
+
+/**
+ * Runs an OS command and save its standard output as a file. Typically used for
+ * MySQL or OpenLDAP dumps.
+ */
+public class OsCallBackup extends AbstractAtomicBackup {
+       private final static Log log = LogFactory.getLog(OsCallBackup.class);
+
+       private String command;
+       private Map<String, String> variables = new HashMap<String, String>();
+       private Executor executor = new DefaultExecutor();
+
+       private Map<String, String> environment = new HashMap<String, String>();
+
+       /** Name of the sudo user, root if "", not sudo if null */
+       private String sudo = null;
+
+       public OsCallBackup() {
+       }
+
+       public OsCallBackup(String name) {
+               super(name);
+       }
+
+       public OsCallBackup(String name, String command) {
+               super(name);
+               this.command = command;
+       }
+
+       @Override
+       public void writeBackup(FileObject targetFo) {
+               String commandToUse = command;
+
+               // sudo
+               if (sudo != null) {
+                       if (sudo.equals(""))
+                               commandToUse = "sudo " + commandToUse;
+                       else
+                               commandToUse = "sudo -u " + sudo + " " + commandToUse;
+               }
+
+               CommandLine commandLine = CommandLine.parse(commandToUse, variables);
+               ByteArrayOutputStream errBos = new ByteArrayOutputStream();
+               if (log.isTraceEnabled())
+                       log.trace(commandLine.toString());
+
+               try {
+                       // stdout
+                       FileContent targetContent = targetFo.getContent();
+                       // stderr
+                       ExecuteStreamHandler streamHandler = new PumpStreamHandler(
+                                       targetContent.getOutputStream(), errBos);
+                       executor.setStreamHandler(streamHandler);
+                       executor.execute(commandLine, environment);
+               } catch (ExecuteException e) {
+                       byte[] err = errBos.toByteArray();
+                       String errStr = new String(err);
+                       throw new ArgeoException("Process " + commandLine + " failed ("
+                                       + e.getExitValue() + "): " + errStr, e);
+               } catch (Exception e) {
+                       byte[] err = errBos.toByteArray();
+                       String errStr = new String(err);
+                       throw new ArgeoException("Process " + commandLine + " failed: "
+                                       + errStr, e);
+               } finally {
+                       IOUtils.closeQuietly(errBos);
+               }
+       }
+
+       public void setCommand(String command) {
+               this.command = command;
+       }
+
+       protected String getCommand() {
+               return command;
+       }
+
+       /**
+        * A reference to the environment variables that will be passed to the
+        * process. Empty by default.
+        */
+       protected Map<String, String> getEnvironment() {
+               return environment;
+       }
+
+       protected Map<String, String> getVariables() {
+               return variables;
+       }
+
+       public void setVariables(Map<String, String> variables) {
+               this.variables = variables;
+       }
+
+       public void setExecutor(Executor executor) {
+               this.executor = executor;
+       }
+
+       public void setSudo(String sudo) {
+               this.sudo = sudo;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/PostgreSqlBackup.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/PostgreSqlBackup.java
new file mode 100644 (file)
index 0000000..37716bb
--- /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.server.backup;
+
+import org.apache.commons.vfs.FileObject;
+
+/** Backups a PostgreSQL database using pg_dump. */
+public class PostgreSqlBackup extends OsCallBackup {
+       /**
+        * PostgreSQL password environment variable (see
+        * http://stackoverflow.com/questions
+        * /2893954/how-to-pass-in-password-to-pg-dump)
+        */
+       protected final static String PGPASSWORD = "PGPASSWORD";
+
+       private String pgDumpLocation = "/usr/bin/pg_dump";
+
+       private String dbUser;
+       private String dbPassword;
+       private String dbName;
+
+       public PostgreSqlBackup() {
+               super();
+       }
+
+       public PostgreSqlBackup(String dbUser, String dbPassword, String dbName) {
+               this.dbUser = dbUser;
+               this.dbPassword = dbPassword;
+               this.dbName = dbName;
+               init();
+       }
+
+       @Override
+       public void init() {
+               // disable compression since pg_dump is used with -Fc option
+               setCompression(null);
+
+               if (getName() == null)
+                       setName(dbName + ".pgdump");
+               super.init();
+       }
+
+       @Override
+       public void writeBackup(FileObject targetFo) {
+               if (getCommand() == null) {
+                       getEnvironment().put(PGPASSWORD, dbPassword);
+                       setCommand(pgDumpLocation + " -Fc" + " -U ${dbUser} ${dbName}");
+               }
+               getVariables().put("dbUser", dbUser);
+               getVariables().put("dbPassword", dbPassword);
+               getVariables().put("dbName", dbName);
+
+               super.writeBackup(targetFo);
+       }
+
+       public void setDbUser(String dbUser) {
+               this.dbUser = dbUser;
+       }
+
+       public void setDbPassword(String dbPassword) {
+               this.dbPassword = dbPassword;
+       }
+
+       public void setDbName(String dbName) {
+               this.dbName = dbName;
+       }
+
+       public void setPgDumpLocation(String mysqldumpLocation) {
+               this.pgDumpLocation = mysqldumpLocation;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/SimpleBackupContext.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/SimpleBackupContext.java
new file mode 100644 (file)
index 0000000..872f31c
--- /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.server.backup;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.commons.vfs.FileSystemManager;
+
+/** Simple implementation of a backup context */
+public class SimpleBackupContext implements BackupContext {
+       private DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmm");
+       private final Date timestamp;
+       private final String name;
+
+       private final FileSystemManager fileSystemManager;
+
+       public SimpleBackupContext(FileSystemManager fileSystemManager,
+                       String backupsBase, String name) {
+               this.name = name;
+               this.timestamp = new Date();
+               this.fileSystemManager = fileSystemManager;
+       }
+
+       public Date getTimestamp() {
+               return timestamp;
+       }
+
+       public String getTimestampAsString() {
+               return dateFormat.format(timestamp);
+       }
+
+       public String getSystemName() {
+               return name;
+       }
+
+       public String getRelativeFolder() {
+               return name + '/' + getTimestampAsString();
+       }
+
+       public DateFormat getDateFormat() {
+               return dateFormat;
+       }
+
+       public FileSystemManager getFileSystemManager() {
+               return fileSystemManager;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/SimpleBackupPurge.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/SimpleBackupPurge.java
new file mode 100644 (file)
index 0000000..035c49c
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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.backup;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemManager;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.Selectors;
+import org.argeo.ArgeoException;
+import org.joda.time.DateTime;
+import org.joda.time.Period;
+
+/** Simple backup purge which keeps backups only for a given number of days */
+public class SimpleBackupPurge implements BackupPurge {
+       private final static Log log = LogFactory.getLog(SimpleBackupPurge.class);
+
+       private Integer daysKept = 30;
+
+       @Override
+       public void purge(FileSystemManager fileSystemManager, String base,
+                       String name, DateFormat dateFormat, FileSystemOptions opts) {
+               try {
+                       DateTime nowDt = new DateTime();
+                       FileObject baseFo = fileSystemManager.resolveFile(
+                                       base + '/' + name, opts);
+
+                       SortedMap<DateTime, FileObject> toDelete = new TreeMap<DateTime, FileObject>();
+                       int backupCount = 0;
+
+                       // make sure base dir exists
+                       baseFo.createFolder();
+
+                       // scan backups and list those which should be deleted
+                       for (FileObject backupFo : baseFo.getChildren()) {
+                               String backupName = backupFo.getName().getBaseName();
+                               Date backupDate = dateFormat.parse(backupName);
+                               backupCount++;
+
+                               DateTime backupDt = new DateTime(backupDate.getTime());
+                               Period sinceThen = new Period(backupDt, nowDt);
+                               int days = sinceThen.getDays();
+                               // int days = sinceThen.getMinutes();
+                               if (days > daysKept) {
+                                       toDelete.put(backupDt, backupFo);
+                               }
+                       }
+
+                       if (toDelete.size() != 0 && toDelete.size() == backupCount) {
+                               // all backups would be deleted
+                               // but we want to keep at least one
+                               DateTime lastBackupDt = toDelete.firstKey();
+                               FileObject keptFo = toDelete.remove(lastBackupDt);
+                               log.warn("Backup " + keptFo
+                                               + " kept although it is older than " + daysKept
+                                               + " days.");
+                       }
+
+                       // delete old backups
+                       for (FileObject backupFo : toDelete.values()) {
+                               backupFo.delete(Selectors.SELECT_ALL);
+                               if (log.isDebugEnabled())
+                                       log.debug("Deleted backup " + backupFo);
+                       }
+               } catch (Exception e) {
+                       throw new ArgeoException("Could not purge previous backups", e);
+               }
+
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/SvnBackup.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/SvnBackup.java
new file mode 100644 (file)
index 0000000..636bbca
--- /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.server.backup;
+
+import java.io.File;
+
+import org.apache.commons.vfs.FileObject;
+
+/** Backups a Subversion repository using svnadmin. */
+public class SvnBackup extends OsCallBackup {
+       private String svnadminLocation = "/usr/bin/svnadmin";
+
+       private String repoLocation;
+       private String repoName;
+
+       public SvnBackup() {
+       }
+
+       public SvnBackup(String repoLocation) {
+               this.repoLocation = repoLocation;
+               init();
+       }
+
+       @Override
+       public void init() {
+               // use directory as repo name
+               if (repoName == null)
+                       repoName = new File(repoLocation).getName();
+
+               if (getName() == null)
+                       setName(repoName + ".svndump");
+               super.init();
+       }
+
+       @Override
+       public void writeBackup(FileObject targetFo) {
+               if (getCommand() == null) {
+                       setCommand(svnadminLocation + " dump " + " ${repoLocation}");
+               }
+               getVariables().put("repoLocation", repoLocation);
+
+               super.writeBackup(targetFo);
+       }
+
+       public void setRepoLocation(String repoLocation) {
+               this.repoLocation = repoLocation;
+       }
+
+       public void setRepoName(String repoName) {
+               this.repoName = repoName;
+       }
+
+       public void setSvnadminLocation(String mysqldumpLocation) {
+               this.svnadminLocation = mysqldumpLocation;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/SystemBackup.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/backup/SystemBackup.java
new file mode 100644 (file)
index 0000000..92f73c7
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * 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.backup;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileSystemManager;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.Selectors;
+import org.apache.commons.vfs.UserAuthenticator;
+import org.apache.commons.vfs.auth.StaticUserAuthenticator;
+import org.apache.commons.vfs.impl.DefaultFileSystemConfigBuilder;
+import org.apache.commons.vfs.impl.StandardFileSystemManager;
+import org.argeo.ArgeoException;
+
+/**
+ * Combines multiple backups and transfer them to a remote location. Purges
+ * remote and local data based on certain criteria.
+ */
+public class SystemBackup implements Runnable {
+       private final static Log log = LogFactory.getLog(SystemBackup.class);
+
+       private FileSystemManager fileSystemManager;
+       private UserAuthenticator userAuthenticator = null;
+
+       private String backupsBase;
+       private String systemName;
+
+       private List<AtomicBackup> atomicBackups = new ArrayList<AtomicBackup>();
+       private BackupPurge backupPurge = new SimpleBackupPurge();
+
+       private Map<String, UserAuthenticator> remoteBases = new HashMap<String, UserAuthenticator>();
+
+       @Override
+       public void run() {
+               if (atomicBackups.size() == 0)
+                       throw new ArgeoException("No atomic backup listed");
+               List<String> failures = new ArrayList<String>();
+
+               SimpleBackupContext backupContext = new SimpleBackupContext(
+                               fileSystemManager, backupsBase, systemName);
+
+               // purge older backups
+               FileSystemOptions opts = new FileSystemOptions();
+               try {
+                       DefaultFileSystemConfigBuilder.getInstance().setUserAuthenticator(
+                                       opts, userAuthenticator);
+               } catch (FileSystemException e) {
+                       throw new ArgeoException("Cannot create authentication", e);
+               }
+
+               try {
+
+                       backupPurge.purge(fileSystemManager, backupsBase, systemName,
+                                       backupContext.getDateFormat(), opts);
+               } catch (Exception e) {
+                       failures.add("Purge " + backupsBase + " failed: " + e.getMessage());
+                       log.error("Purge of " + backupsBase + " failed", e);
+               }
+
+               // perform backup
+               for (AtomicBackup atomickBackup : atomicBackups) {
+                       try {
+                               String target = atomickBackup.backup(fileSystemManager,
+                                               backupsBase, backupContext, opts);
+                               if (log.isDebugEnabled())
+                                       log.debug("Performed backup " + target);
+                       } catch (Exception e) {
+                               String msg = "Atomic backup " + atomickBackup.getName()
+                                               + " failed: " + ArgeoException.chainCausesMessages(e);
+                               failures.add(msg);
+                               log.error(msg);
+                               if (log.isTraceEnabled())
+                                       log.trace(
+                                                       "Stacktrace of atomic backup "
+                                                                       + atomickBackup.getName() + " failure.", e);
+                       }
+               }
+
+               // dispatch to remote
+               for (String remoteBase : remoteBases.keySet()) {
+                       FileObject localBaseFo = null;
+                       FileObject remoteBaseFo = null;
+                       UserAuthenticator auth = remoteBases.get(remoteBase);
+
+                       // authentication
+                       FileSystemOptions remoteOpts = new FileSystemOptions();
+                       try {
+                               DefaultFileSystemConfigBuilder.getInstance()
+                                               .setUserAuthenticator(remoteOpts, auth);
+                               backupPurge.purge(fileSystemManager, remoteBase, systemName,
+                                               backupContext.getDateFormat(), remoteOpts);
+                       } catch (Exception e) {
+                               failures.add("Purge " + remoteBase + " failed: "
+                                               + e.getMessage());
+                               log.error("Cannot purge " + remoteBase, e);
+                       }
+
+                       try {
+                               localBaseFo = fileSystemManager.resolveFile(backupsBase + '/'
+                                               + backupContext.getRelativeFolder(), opts);
+                               remoteBaseFo = fileSystemManager.resolveFile(remoteBase + '/'
+                                               + backupContext.getRelativeFolder(), remoteOpts);
+                               remoteBaseFo.copyFrom(localBaseFo, Selectors.SELECT_ALL);
+                               if (log.isDebugEnabled())
+                                       log.debug("Copied backup to " + remoteBaseFo + " from "
+                                                       + localBaseFo);
+                               // }
+                       } catch (Exception e) {
+                               failures.add("Dispatch to " + remoteBase + " failed: "
+                                               + e.getMessage());
+                               log.error(
+                                               "Cannot dispatch backups from "
+                                                               + backupContext.getRelativeFolder() + " to "
+                                                               + remoteBase, e);
+                       }
+                       BackupUtils.closeFOQuietly(localBaseFo);
+                       BackupUtils.closeFOQuietly(remoteBaseFo);
+               }
+
+               int failureCount = 0;
+               if (failures.size() > 0) {
+                       StringBuffer buf = new StringBuffer();
+                       for (String failure : failures) {
+                               buf.append('\n').append(failureCount).append(" - ")
+                                               .append(failure);
+                               failureCount++;
+                       }
+                       throw new ArgeoException(failureCount
+                                       + " error(s) when running the backup,"
+                                       + " check the logs and the backups as soon as possible."
+                                       + buf);
+               }
+       }
+
+       public void setFileSystemManager(FileSystemManager fileSystemManager) {
+               this.fileSystemManager = fileSystemManager;
+       }
+
+       public void setBackupsBase(String backupsBase) {
+               this.backupsBase = backupsBase;
+       }
+
+       public void setSystemName(String name) {
+               this.systemName = name;
+       }
+
+       public void setAtomicBackups(List<AtomicBackup> atomicBackups) {
+               this.atomicBackups = atomicBackups;
+       }
+
+       public void setBackupPurge(BackupPurge backupPurge) {
+               this.backupPurge = backupPurge;
+       }
+
+       public void setUserAuthenticator(UserAuthenticator userAuthenticator) {
+               this.userAuthenticator = userAuthenticator;
+       }
+
+       public void setRemoteBases(Map<String, UserAuthenticator> remoteBases) {
+               this.remoteBases = remoteBases;
+       }
+
+       public static void main(String args[]) {
+               while (true) {
+                       try {
+                               StandardFileSystemManager fsm = new StandardFileSystemManager();
+                               fsm.init();
+
+                               SystemBackup systemBackup = new SystemBackup();
+                               systemBackup.setSystemName("mySystem");
+                               systemBackup
+                                               .setBackupsBase("/home/mbaudier/dev/src/commons/server/runtime/org.argeo.server.core/target");
+                               systemBackup.setFileSystemManager(fsm);
+
+                               List<AtomicBackup> atomicBackups = new ArrayList<AtomicBackup>();
+
+                               MySqlBackup mySqlBackup = new MySqlBackup("root", "", "test");
+                               atomicBackups.add(mySqlBackup);
+                               PostgreSqlBackup postgreSqlBackup = new PostgreSqlBackup(
+                                               "argeo", "argeo", "gis_template");
+                               atomicBackups.add(postgreSqlBackup);
+                               SvnBackup svnBackup = new SvnBackup(
+                                               "/home/mbaudier/tmp/testsvnrepo");
+                               atomicBackups.add(svnBackup);
+
+                               systemBackup.setAtomicBackups(atomicBackups);
+
+                               Map<String, UserAuthenticator> remoteBases = new HashMap<String, UserAuthenticator>();
+                               StaticUserAuthenticator userAuthenticator = new StaticUserAuthenticator(
+                                               null, "demo", "demo");
+                               remoteBases.put("sftp://localhost/home/mbaudier/test",
+                                               userAuthenticator);
+                               systemBackup.setRemoteBases(remoteBases);
+
+                               systemBackup.run();
+
+                               fsm.close();
+                       } catch (FileSystemException e) {
+                               // TODO Auto-generated catch block
+                               e.printStackTrace();
+                               System.exit(1);
+                       }
+
+                       // wait
+                       try {
+                               Thread.sleep(120 * 1000);
+                       } catch (InterruptedException e) {
+                               e.printStackTrace();
+                       }
+               }
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/AbstractMemoryDaoSupport.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/AbstractMemoryDaoSupport.java
new file mode 100644 (file)
index 0000000..f9af51b
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * 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.dao;
+
+import java.beans.PropertyEditor;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.io.Resource;
+
+public abstract class AbstractMemoryDaoSupport implements LightDaoSupport,
+               ApplicationContextAware, InitializingBean {
+       private final static Log log = LogFactory
+                       .getLog(AbstractMemoryDaoSupport.class);
+
+       private ClassLoader classLoader = getClass().getClassLoader();
+       private ApplicationContext applicationContext;
+       private List<Class<?>> additionalClasses = new ArrayList<Class<?>>();
+
+       private Map<Class<?>, Map<Object, Object>> model = new HashMap<Class<?>, Map<Object, Object>>();
+
+       private Map<String, Object> externalRefs = new HashMap<String, Object>();
+
+       private List<String> scannedPackages = new ArrayList<String>();
+
+       private List<Resource> resources = new ArrayList<Resource>();
+
+       private Map<Class<?>, PropertyEditor> customEditors = new HashMap<Class<?>, PropertyEditor>();;
+
+       protected abstract void load(InputStream in, List<Reference> references);
+
+       protected abstract Object findInternalRef(Reference reference);
+
+       public void afterPropertiesSet() throws Exception {
+               init();
+       }
+
+       public void init() {
+               for (PropertyEditor propertyEditor : customEditors.values())
+                       if (propertyEditor instanceof LightDaoAware) {
+                               ((LightDaoAware) propertyEditor).setLightDaoSupport(this);
+                       }
+
+               // Load data
+               List<Reference> references = new ArrayList<Reference>();
+
+               for (Resource res : resources) {
+                       InputStream in = null;
+                       try {
+                               in = res.getInputStream();
+                               load(in, references);
+                       } catch (Exception e) {
+                               throw new ArgeoException("Cannot load stream", e);
+                       } finally {
+                               IOUtils.closeQuietly(in);
+                       }
+               }
+
+               // Inject references
+               for (Reference ref : references) {
+                       injectReference(ref);
+               }
+               if (log.isDebugEnabled())
+                       log.debug(references.size() + " references linked");
+       }
+
+       public List<Class<?>> getSupportedClasses() {
+               List<Class<?>> res = new ArrayList<Class<?>>();
+               res.addAll(additionalClasses);
+               res.addAll(model.keySet());
+               return res;
+       }
+
+       protected void injectReference(Reference reference) {
+               BeanWrapper bw = new BeanWrapperImpl(reference.object);
+               Object targetObject;
+               if (reference.getExternalRef() != null) {
+                       String ref = reference.getExternalRef();
+                       if (externalRefs.containsKey(ref))
+                               targetObject = externalRefs.get(ref);
+                       else if (applicationContext != null)
+                               targetObject = applicationContext.getBean(ref);
+                       else {
+                               targetObject = null;
+                               log.warn("Ref " + ref + " not found");
+                       }
+               } else {
+                       targetObject = findInternalRef(reference);
+               }
+               bw.setPropertyValue(reference.property, targetObject);
+
+       }
+
+       protected BeanWrapper newBeanWrapper(Class<?> targetClass) {
+               BeanWrapperImpl bw = new BeanWrapperImpl(targetClass);
+               for (Class<?> clss : customEditors.keySet())
+                       bw.registerCustomEditor(clss, customEditors.get(clss));
+               return bw;
+       }
+
+       @SuppressWarnings("unchecked")
+       public <T> T getByKey(Class<T> clss, Object key) {
+               if (key == null)
+                       throw new ArgeoException("Key is null for " + clss);
+               return (T) model.get(findClass(clss)).get(key);
+       }
+
+       /**
+        * Slow.
+        * 
+        * @return the first found
+        */
+       public <T> T getByField(Class<T> clss, String field, Object value) {
+               List<T> all = list(clss, null);
+               T res = null;
+               for (T obj : all) {
+                       if (new BeanWrapperImpl(obj).getPropertyValue(field).equals(value)) {
+                               res = obj;
+                               break;
+                       }
+               }
+               return res;
+       }
+
+       @SuppressWarnings({ "unchecked", "rawtypes" })
+       public <T> List<T> list(Class<T> clss, Object filter) {
+               List<T> res = new ArrayList<T>();
+
+               Class classToUse = findClass(clss);
+               if (classToUse != null)
+                       res.addAll((Collection<T>) model.get(classToUse).values());
+
+               if (applicationContext != null)
+                       res.addAll(applicationContext.getBeansOfType(clss).values());
+
+               return res;
+       }
+
+       @SuppressWarnings({ "unchecked", "rawtypes" })
+       protected Class findClass(Class parent) {
+               if (model.containsKey(parent))
+                       return parent;
+
+               for (Class clss : model.keySet()) {
+                       if (parent.isAssignableFrom(clss))
+                               return clss;// return the first found
+               }
+               if (log.isDebugEnabled())
+                       log.warn("No class found for " + parent.getName());
+               return null;
+       }
+
+       public void setApplicationContext(ApplicationContext applicationContext)
+                       throws BeansException {
+               this.applicationContext = applicationContext;
+       }
+
+       /**
+        * When it should be stored under a different class (e.g. super class or
+        * interface)
+        */
+       public void saveOrUpdate(Object key, Object value, Class<?> clss) {
+               if (!model.containsKey(clss))
+                       model.put(clss, new TreeMap<Object, Object>());
+               model.get(clss).put(key, value);
+       }
+
+       protected ClassLoader getClassLoader() {
+               return classLoader;
+       }
+
+       public void setExternalRefs(Map<String, Object> externalRefs) {
+               this.externalRefs = externalRefs;
+       }
+
+       public Map<String, Object> getExternalRefs() {
+               return externalRefs;
+       }
+
+       public void setScannedPackages(List<String> scannedPackages) {
+               this.scannedPackages = scannedPackages;
+       }
+
+       public List<String> getScannedPackages() {
+               return scannedPackages;
+       }
+
+       public void setResources(List<Resource> workbooks) {
+               this.resources = workbooks;
+       }
+
+       public List<Resource> getResources() {
+               return resources;
+       }
+
+       public void setClassLoader(ClassLoader classLoader) {
+               this.classLoader = classLoader;
+       }
+
+       public List<Class<?>> getAdditionalClasses() {
+               return additionalClasses;
+       }
+
+       public void setAdditionalClasses(List<Class<?>> additionalClasses) {
+               this.additionalClasses = additionalClasses;
+       }
+
+       public void setCustomEditors(Map<Class<?>, PropertyEditor> propertyEditors) {
+               this.customEditors = propertyEditors;
+       }
+
+       protected static class Reference {
+               private Object object;
+               private String property;
+               private String externalRef;
+
+               public Reference(Object object, String property, String externalRef) {
+                       this.object = object;
+                       this.property = property;
+                       this.externalRef = externalRef;
+               }
+
+               public Object getObject() {
+                       return object;
+               }
+
+               public String getProperty() {
+                       return property;
+               }
+
+               public String getExternalRef() {
+                       return externalRef;
+               }
+
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/AbstractTabularDaoSupport.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/AbstractTabularDaoSupport.java
new file mode 100644 (file)
index 0000000..1ca5a10
--- /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.server.dao;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+
+public abstract class AbstractTabularDaoSupport extends
+               AbstractMemoryDaoSupport {
+       private final static Log log = LogFactory
+                       .getLog(AbstractTabularDaoSupport.class);
+       
+       private Map<String, List<Object>> tabularView = new HashMap<String, List<Object>>();
+
+       @Override
+       protected Object findInternalRef(Reference reference) {
+               TabularInternalReference tabReference = (TabularInternalReference) reference;
+               return getFromTabularView(tabReference.getTargetTableName(),
+                               tabReference.getTargetRow());
+       }
+
+       protected Object getFromTabularView(String tableName, Integer row) {
+               return tabularView.get(tableName).get(row - 2);
+       }
+
+       protected void registerInTabularView(String tableName, Object object) {
+               if (!tabularView.containsKey(tableName))
+                       tabularView.put(tableName, new ArrayList<Object>());
+               tabularView.get(tableName).add(object);
+       }
+
+       protected Class<?> findClassToInstantiate(String tableName) {
+               // TODO: ability to map sheet names and class names
+               String className = tableName;
+               Class<?> clss = null;
+               try {
+                       clss = getClassLoader().loadClass(className);
+                       return clss;
+               } catch (ClassNotFoundException e) {
+                       // silent
+               }
+
+               scannedPkgs: for (String pkg : getScannedPackages()) {
+                       try {
+                               clss = getClassLoader().loadClass(pkg.trim() + "." + className);
+                               break scannedPkgs;
+                       } catch (ClassNotFoundException e) {
+                               // silent
+                               if (log.isTraceEnabled())
+                                       log.trace(e.getMessage());
+                       }
+               }
+
+               if (clss == null)
+                       throw new ArgeoException("Cannot find a class for table "
+                                       + tableName);
+
+               return clss;
+       }
+
+       protected static class TabularInternalReference extends Reference {
+               private String targetTableName;
+               private Integer targetRow;
+
+               public TabularInternalReference(Object object, String property,
+                               String targetSheet, Integer targetRow) {
+                       super(object, property, null);
+                       this.targetTableName = targetSheet;
+                       this.targetRow = targetRow;
+               }
+
+               public String getTargetTableName() {
+                       return targetTableName;
+               }
+
+               public Integer getTargetRow() {
+                       return targetRow;
+               }
+
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/LightDaoAware.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/LightDaoAware.java
new file mode 100644 (file)
index 0000000..5ae7bea
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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.dao;
+
+public interface LightDaoAware {
+       public void setLightDaoSupport(LightDaoSupport lightDaoSupport);
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/LightDaoPropertyEditor.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/LightDaoPropertyEditor.java
new file mode 100644 (file)
index 0000000..de6ba70
--- /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.server.dao;
+
+import java.beans.PropertyEditorSupport;
+
+import org.argeo.ArgeoException;
+
+public class LightDaoPropertyEditor extends PropertyEditorSupport implements
+               LightDaoAware {
+       private LightDaoSupport lightDaoSupport;
+
+       private Class<?> targetClass;
+       /** Can be null */
+       private String businessIdField;
+
+       @Override
+       public String getAsText() {
+               return getValue().toString();
+       }
+
+       @Override
+       public void setAsText(String text) throws IllegalArgumentException {
+               if (targetClass == null)
+                       throw new ArgeoException("Target class cannot be null");
+
+               if (businessIdField != null)
+                       setValue(lightDaoSupport.getByField(targetClass, businessIdField,
+                                       text));
+               else
+                       setValue(lightDaoSupport.getByKey(targetClass, text));
+       }
+
+       public void setLightDaoSupport(LightDaoSupport lightDaoSupport) {
+               this.lightDaoSupport = lightDaoSupport;
+       }
+
+       public void setTargetClass(Class<?> targetClass) {
+               this.targetClass = targetClass;
+       }
+
+       public void setBusinessIdField(String businessIdField) {
+               this.businessIdField = businessIdField;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/LightDaoSupport.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/dao/LightDaoSupport.java
new file mode 100644 (file)
index 0000000..6ea1b75
--- /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.server.dao;
+
+import java.util.List;
+
+/** Minimal generic DAO for easy to implements objects <-> storage mapping. */
+public interface LightDaoSupport {
+       /** Retrieve an object of a given type by its unique key. */
+       public <T> T getByKey(Class<T> clss, Object key);
+
+       /** Retrieve an object of a given type by the value of one of its fields. */
+       public <T> T getByField(Class<T> clss, String field, Object value);
+
+       /** List all objects, optionally filtering them (implementation dependent) */
+       public <T> List<T> list(Class<T> clss, Object filter);
+
+       /** Lis all supported object types. */
+       public List<Class<?>> getSupportedClasses();
+
+       /** Save or update an object */
+       public void saveOrUpdate(Object key, Object value, Class<?> clss);
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/DefaultHandlerExceptionResolver.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/DefaultHandlerExceptionResolver.java
new file mode 100644 (file)
index 0000000..6269bd0
--- /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.server.mvc;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.server.ServerAnswer;
+import org.springframework.web.servlet.HandlerExceptionResolver;
+import org.springframework.web.servlet.ModelAndView;
+
+public class DefaultHandlerExceptionResolver implements
+               HandlerExceptionResolver {
+       private final static Log log = LogFactory
+                       .getLog(DefaultHandlerExceptionResolver.class);
+
+       public ModelAndView resolveException(HttpServletRequest request,
+                       HttpServletResponse response, Object handler, Exception ex) {
+               ModelAndView mv = new ModelAndView();
+               ServerAnswer serverAnswer = ServerAnswer.error(ex);
+               mv.addObject(serverAnswer);
+
+               if (log.isDebugEnabled())
+                       log.error(serverAnswer);
+
+               mv.setViewName("500");
+               // response.setStatus(500);
+               return mv;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/EmptyViewController.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/EmptyViewController.java
new file mode 100644 (file)
index 0000000..c83d4ae
--- /dev/null
@@ -0,0 +1,34 @@
+/*\r
+ * Copyright (C) 2007-2012 Argeo GmbH\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *         http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.argeo.server.mvc;\r
+\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import org.springframework.web.servlet.ModelAndView;\r
+import org.springframework.web.servlet.mvc.ParameterizableViewController;\r
+\r
+/** Dummy controller which simply redirects to a view (typically a JSP)*/\r
+public class EmptyViewController extends ParameterizableViewController {\r
+\r
+       protected ModelAndView handleRequestInternal(HttpServletRequest request,\r
+                       HttpServletResponse response) throws Exception {\r
+               ModelAndView modelAndView = new ModelAndView();\r
+               modelAndView.setViewName(getViewName());\r
+               return modelAndView;\r
+\r
+       }\r
+}\r
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/MvcConstants.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/MvcConstants.java
new file mode 100644 (file)
index 0000000..d5c32dc
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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.mvc;
+
+public interface MvcConstants {
+       public final static String ANSWER_MODEL_KEY = "org.argeo.server.mvc.ANSWER";
+       public final static String ANSWER_MODEL_KEY_AS_HTML = "org.argeo.server.mvc.ANSWER_AS_HTML";
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/SerializingView.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/SerializingView.java
new file mode 100644 (file)
index 0000000..b42a9a0
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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.mvc;
+
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.argeo.ArgeoException;
+import org.argeo.server.ServerAnswer;
+import org.argeo.server.ServerSerializer;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.servlet.View;
+import org.springframework.web.servlet.view.AbstractView;
+
+/**
+ * Can be used as a standalone {@link View} or using
+ * {@link SerializingViewResolver}
+ */
+public class SerializingView extends AbstractView implements MvcConstants {
+       private final String viewName;
+       private final Locale locale;
+
+       private ServerSerializer serializer;
+
+       public SerializingView() {
+               this.viewName = null;
+               this.locale = Locale.getDefault();
+       }
+
+       public SerializingView(String viewName, Locale locale,
+                       ServerSerializer serializer) {
+               this.viewName = viewName;
+               this.locale = locale;
+               this.serializer = serializer;
+       }
+
+       @SuppressWarnings({ "rawtypes" })
+       @Override
+       protected void renderMergedOutputModel(Map model,
+                       HttpServletRequest request, HttpServletResponse response)
+                       throws Exception {
+               Boolean serverAnswersAsHtml = model
+                               .containsKey(ANSWER_MODEL_KEY_AS_HTML);
+
+               final Object answer = findAnswerInModel(model);
+
+               if ((answer instanceof ServerAnswer) && serverAnswersAsHtml) {
+                       response.setContentType("text/html");
+                       ServerAnswer serverAnswer = (ServerAnswer) answer;
+                       response.getWriter().append("<pre>");
+                       response.getWriter().append(serverAnswer.getMessage());
+                       response.getWriter().append("</pre>");
+               } else {
+                       serializer.serialize(answer, request, response);
+               }
+       }
+
+       @SuppressWarnings("rawtypes")
+       protected Object findAnswerInModel(Map model) {
+               if (model.size() == 1) {
+                       return model.values().iterator().next();
+               } else if (model.size() == 2) {
+                       boolean otherIsBindingResult = false;
+                       Object answerValue = null;
+                       for (Object value : model.values()) {
+                               if (value instanceof BindingResult)
+                                       otherIsBindingResult = true;
+                               else
+                                       answerValue = value;
+                       }
+
+                       if (otherIsBindingResult)
+                               return answerValue;
+               }
+
+               if (model.containsKey(ANSWER_MODEL_KEY)) {
+                       return model.get(ANSWER_MODEL_KEY);
+               } else if (model.containsKey(ANSWER_MODEL_KEY_AS_HTML)) {
+                       return model.get(ANSWER_MODEL_KEY_AS_HTML);
+               } else if (viewName != null && model.containsKey(viewName)) {
+                       return model.get(viewName);
+               } else {
+                       if (model.size() == 0)
+                               throw new ArgeoException("Model is empty.");
+                       else
+                               throw new ArgeoException(
+                                               "Model has a size different from 1. Specify a modelKey.");
+               }
+       }
+
+       public String getViewName() {
+               return viewName;
+       }
+
+       public Locale getLocale() {
+               return locale;
+       }
+
+       public void setSerializer(ServerSerializer serializer) {
+               this.serializer = serializer;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/SerializingViewResolver.java b/trunk/server/runtime/org.argeo.server.core/src/main/java/org/argeo/server/mvc/SerializingViewResolver.java
new file mode 100644 (file)
index 0000000..9331543
--- /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.server.mvc;
+
+import java.util.Locale;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.server.ServerSerializer;
+import org.springframework.web.servlet.View;
+import org.springframework.web.servlet.view.AbstractCachingViewResolver;
+
+/**
+ * Returns a {@link SerializingView} based on the underlying.
+ */
+public class SerializingViewResolver extends AbstractCachingViewResolver {
+       private final static Log log = LogFactory
+                       .getLog(SerializingViewResolver.class);
+
+       private ServerSerializer serializer;
+
+       @Override
+       protected View loadView(String viewName, Locale locale) throws Exception {
+               if (log.isTraceEnabled())
+                       log.trace("viewName=" + viewName);
+               return new SerializingView(viewName, locale, serializer);
+       }
+
+       public void setSerializer(ServerSerializer serializer) {
+               this.serializer = serializer;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.hibernate/.classpath b/trunk/server/runtime/org.argeo.server.hibernate/.classpath
new file mode 100644 (file)
index 0000000..ff41fbb
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/trunk/server/runtime/org.argeo.server.hibernate/.project b/trunk/server/runtime/org.argeo.server.hibernate/.project
new file mode 100644 (file)
index 0000000..8252672
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.hibernate</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/trunk/server/runtime/org.argeo.server.hibernate/.settings/org.eclipse.jdt.core.prefs b/trunk/server/runtime/org.argeo.server.hibernate/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..405317e
--- /dev/null
@@ -0,0 +1,5 @@
+#Tue Oct 13 14:51:57 CEST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/server/runtime/org.argeo.server.hibernate/.settings/org.maven.ide.eclipse.prefs b/trunk/server/runtime/org.argeo.server.hibernate/.settings/org.maven.ide.eclipse.prefs
new file mode 100644 (file)
index 0000000..c523f7b
--- /dev/null
@@ -0,0 +1,9 @@
+#Tue Oct 13 14:51:50 CEST 2009
+activeProfiles=
+eclipse.preferences.version=1
+fullBuildGoals=process-test-resources
+includeModules=false
+resolveWorkspaceProjects=true
+resourceFilterGoals=process-resources resources\:testResources
+skipCompilerPlugin=true
+version=1
diff --git a/trunk/server/runtime/org.argeo.server.hibernate/build.properties b/trunk/server/runtime/org.argeo.server.hibernate/build.properties
new file mode 100644 (file)
index 0000000..68ef43c
--- /dev/null
@@ -0,0 +1,2 @@
+additional.bundles = org.springframework.core
+source.. = src/main/java/
diff --git a/trunk/server/runtime/org.argeo.server.hibernate/pom.xml b/trunk/server/runtime/org.argeo.server.hibernate/pom.xml
new file mode 100644 (file)
index 0000000..44fa4f9
--- /dev/null
@@ -0,0 +1,71 @@
+<?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.server</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.hibernate</artifactId>
+       <name>Commons Server Hibernate</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>
+                                                       org.argeo.*
+                                               </Export-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.core</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Hibernate -->
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.dep.hibernate</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>net.sf.ehcache</artifactId>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.orm</artifactId>
+               </dependency>
+               <dependency>
+                       <!-- For EhCache support -->
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.context.support</artifactId>
+               </dependency>
+
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.hibernate/src/main/java/org/argeo/server/hibernate/HibernateLightDaoSync.java b/trunk/server/runtime/org.argeo.server.hibernate/src/main/java/org/argeo/server/hibernate/HibernateLightDaoSync.java
new file mode 100644 (file)
index 0000000..365565f
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.hibernate;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.server.dao.LightDaoSupport;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+
+public class HibernateLightDaoSync {
+       private final static Log log = LogFactory
+                       .getLog(HibernateLightDaoSync.class);
+
+       private String externalSuffix = LightDaoInterceptor.DEFAULT_EXTERNAL_SUFFIX;
+
+       private SessionFactory sessionFactory;
+
+       private LightDaoSupport lightDaoSupport;
+
+       private List<Class<?>> classes = new ArrayList<Class<?>>();
+
+       public void sync() {
+               List<Class<?>> lst;
+               if (classes.size() > 0)
+                       lst = classes;
+               else
+                       lst = lightDaoSupport.getSupportedClasses();
+
+               Session session = sessionFactory.getCurrentSession();
+               session.beginTransaction();
+               try {
+                       for (Class<?> clss : lst) {
+                               String entityName = clss.getSimpleName() + externalSuffix;
+                               int count = 0;
+                               for (Object obj : lightDaoSupport.list(clss, null)) {
+                                       session.save(entityName, obj);
+                                       count++;
+                               }
+                               if (log.isDebugEnabled())
+                                       log.debug("Synchronized " + count + "\tentities '"
+                                                       + entityName + "'");
+                       }
+                       session.getTransaction().commit();
+               } catch (Exception e) {
+                       session.getTransaction().rollback();
+               }
+       }
+
+       public void setClasses(List<Class<?>> classes) {
+               this.classes = classes;
+       }
+
+       public void setExternalSuffix(String externalSuffix) {
+               this.externalSuffix = externalSuffix;
+       }
+
+       public void setSessionFactory(SessionFactory sessionFactory) {
+               this.sessionFactory = sessionFactory;
+       }
+
+       public void setLightDaoSupport(LightDaoSupport lightDaoSupport) {
+               this.lightDaoSupport = lightDaoSupport;
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.hibernate/src/main/java/org/argeo/server/hibernate/LightDaoInterceptor.java b/trunk/server/runtime/org.argeo.server.hibernate/src/main/java/org/argeo/server/hibernate/LightDaoInterceptor.java
new file mode 100644 (file)
index 0000000..868ae39
--- /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.server.hibernate;
+
+import java.beans.PropertyDescriptor;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.server.dao.LightDaoSupport;
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.type.Type;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+
+public class LightDaoInterceptor extends EmptyInterceptor {
+       private final static Log log = LogFactory.getLog(LightDaoInterceptor.class);
+
+       private static final long serialVersionUID = 1L;
+
+       public final static String DEFAULT_EXTERNAL_SUFFIX = "_external";
+
+       private String externalSuffix = DEFAULT_EXTERNAL_SUFFIX;
+
+       private LightDaoSupport lightDaoSupport;
+
+       private List<Class<?>> classes = new ArrayList<Class<?>>();
+
+       private Map<Class<?>, String> businessIdFields = new HashMap<Class<?>, String>();
+
+       /** internal */
+       private final Map<Class<?>, Map<Serializable, Object>> bidMappings = new HashMap<Class<?>, Map<Serializable, Object>>();
+
+       @Override
+       public Object getEntity(String entityName, Serializable id) {
+               Class<?> clss = findSupportingClass(entityName);
+               Object res = null;
+               if (clss != null) {
+                       if (businessIdFields.containsKey(clss)) {
+                               String field = businessIdFields.get(clss);
+                               Object value = bidMappings.get(clss).get(id);
+                               res = lightDaoSupport.getByField(clss, field, value);
+                               if (log.isTraceEnabled())
+                                       log.debug("Got entity " + clss + " (" + field + "=" + value
+                                                       + ")");
+                       } else {
+                               res = lightDaoSupport.getByKey(clss, id);
+                               if (log.isTraceEnabled())
+                                       log.debug("Got entity " + clss + " (id=" + id + ")");
+                       }
+               } else {
+                       res = super.getEntity(entityName, id);
+               }
+               return res;
+       }
+
+       @Override
+       public String getEntityName(Object object) {
+               if (supports(object)) {
+                       return toExternalName(object.getClass());
+               } else {
+                       return super.getEntityName(object);
+               }
+       }
+
+       @Override
+       public boolean onSave(Object entity, Serializable id, Object[] state,
+                       String[] propertyNames, Type[] types) {
+               if (supports(entity)) {
+                       Class<?> clss = entity.getClass();
+                       if (businessIdFields.containsKey(clss)) {
+                               if (!bidMappings.containsKey(clss))
+                                       bidMappings.put(clss, new HashMap<Serializable, Object>());
+                               BeanWrapper bw = new BeanWrapperImpl(entity);
+                               Object bid = bw.getPropertyValue(businessIdFields.get(clss));
+                               bidMappings.get(clss).put(id, bid);
+                               if (log.isTraceEnabled())
+                                       log.debug("Mapped tid " + id + " with bid " + bid + " for "
+                                                       + clss);
+                       }
+               }
+               return super.onSave(entity, id, state, propertyNames, types);
+       }
+
+       @Override
+       public boolean onLoad(Object entity, Serializable id, Object[] state,
+                       String[] propertyNames, Type[] types) {
+               Class<?> clss = entity.getClass();
+               Object source = null;
+               if (lightDaoSupport.getSupportedClasses().contains(clss)) {
+                       if (businessIdFields.containsKey(clss)) {
+                               String field = businessIdFields.get(clss);
+                               Object value = bidMappings.get(clss).get(id);
+                               source = lightDaoSupport.getByField(clss, field, value);
+                               if (log.isTraceEnabled())
+                                       log.debug("Loading entity " + clss + " (" + field + "="
+                                                       + value + ")");
+                       } else {
+                               source = lightDaoSupport.getByKey(clss, id);
+                               if (log.isTraceEnabled())
+                                       log.debug("Loading entity " + clss + " (id=" + id + ")");
+                       }
+               }
+
+               if (source != null) {
+                       BeanWrapper bwTarget = new BeanWrapperImpl(entity);
+                       BeanWrapper bwSource = new BeanWrapperImpl(source);
+                       for (PropertyDescriptor pd : bwTarget.getPropertyDescriptors()) {
+                               String propName = pd.getName();
+                               if (bwSource.isReadableProperty(propName)
+                                               && bwTarget.isWritableProperty(propName)) {
+                                       bwTarget.setPropertyValue(propName, bwSource
+                                                       .getPropertyValue(propName));
+                                       if (log.isTraceEnabled())
+                                               log.debug("Loaded property " + propName + " for class "
+                                                               + clss + " (id=" + id + ")");
+                               }
+                       }
+
+                       return true;
+               } else {
+                       // res = super.getEntity(entityName, id);
+                       return super.onLoad(entity, id, state, propertyNames, types);
+               }
+       }
+
+       protected Boolean supports(Object object) {
+               if (classes.contains(object.getClass()))
+                       return lightDaoSupport.getSupportedClasses().contains(
+                                       object.getClass());
+               else
+                       return false;
+       }
+
+       /** @return null if not found */
+       protected Class<?> findSupportingClass(String entityName) {
+               for (Class<?> clss : lightDaoSupport.getSupportedClasses()) {
+                       if (toExternalName(clss).equals(entityName)) {
+                               if (classes.contains(clss))
+                                       return clss;
+                       }
+               }
+               return null;
+       }
+
+       protected final String toExternalName(Class<?> clss) {
+               return clss.getSimpleName() + externalSuffix;
+       }
+
+       public void setExternalSuffix(String externalSuffix) {
+               this.externalSuffix = externalSuffix;
+       }
+
+       public void setLightDaoSupport(LightDaoSupport lightDaoSupport) {
+               this.lightDaoSupport = lightDaoSupport;
+       }
+
+       public void setClasses(List<Class<?>> classes) {
+               this.classes = classes;
+       }
+
+       public void setBusinessIdFields(Map<Class<?>, String> businessIdFields) {
+               this.businessIdFields = businessIdFields;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/.classpath b/trunk/server/runtime/org.argeo.server.jackrabbit/.classpath
new file mode 100644 (file)
index 0000000..e9c521a
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+       <classpathentry kind="src" output="target/classes" path="src/main/resources"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/.project b/trunk/server/runtime/org.argeo.server.jackrabbit/.project
new file mode 100644 (file)
index 0000000..a381d35
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.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.jdt.core.javanature</nature>
+               <nature>org.eclipse.pde.PluginNature</nature>
+       </natures>
+</projectDescription>
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/build.properties b/trunk/server/runtime/org.argeo.server.jackrabbit/build.properties
new file mode 100644 (file)
index 0000000..722c27c
--- /dev/null
@@ -0,0 +1,10 @@
+additional.bundles = slf4j.api,\
+                     slf4j.log4j,\
+                     org.apache.log4j,\
+                     org.apache.commons.collections,\
+                     edu.oswego.cs.dl.util.concurrent,\
+                     org.h2,\
+                     org.apache.lucene,\
+                     org.springframework.context
+source.. = src/main/java/,\
+           src/main/resources/
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/pom.xml b/trunk/server/runtime/org.argeo.server.jackrabbit/pom.xml
new file mode 100644 (file)
index 0000000..2d365c7
--- /dev/null
@@ -0,0 +1,103 @@
+<?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.server</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.jackrabbit</artifactId>
+       <name>Commons Server Jackrabbit</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>
+                                                       org.argeo.jackrabbit.*,
+                                               </Export-Package>
+                                               <Import-Package>
+                                                       javax.servlet,
+                                                       org.apache.jackrabbit.webdav.server,
+                                                       org.springframework.web.context,
+                                                       org.xml.sax;version="0.0.0",
+                                                       org.apache.jackrabbit.webdav.jcr,
+                                                       org.springframework.beans,
+                                                       *
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.jcr.mvc</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Jackrabbit -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.jackrabbit</artifactId>
+                       <type>pom</type>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- OSGi -->
+               <dependency>
+                       <groupId>org.argeo.tp.rap.platform</groupId>
+                       <artifactId>org.eclipse.osgi</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.beans</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.web.servlet</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.security.core</artifactId>
+               </dependency>
+
+               <!-- TEST -->
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.support.junit</artifactId>
+                       <version>2.1.11</version>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.log4j</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+                       <scope>test</scope>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitAuthorizations.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitAuthorizations.java
new file mode 100644 (file)
index 0000000..e880b67
--- /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;
+
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.security.JcrAuthorizations;
+
+/** Apply authorizations to a Jackrabbit repository. */
+public class JackrabbitAuthorizations extends JcrAuthorizations {
+       private final static Log log = LogFactory
+                       .getLog(JackrabbitAuthorizations.class);
+
+       private List<String> groupPrefixes = Arrays
+                       .asList(new String[] { "ROLE_" });// new ArrayList<String>();
+
+       @Override
+       protected Principal getOrCreatePrincipal(Session session,
+                       String principalName) throws RepositoryException {
+               UserManager um = ((JackrabbitSession) session).getUserManager();
+               synchronized (um) {
+                       Authorizable authorizable = um.getAuthorizable(principalName);
+                       if (authorizable == null) {
+                               groupPrefixes: for (String groupPrefix : groupPrefixes) {
+                                       if (principalName.startsWith(groupPrefix)) {
+                                               authorizable = um.createGroup(principalName);
+                                               log.info("Created group " + principalName);
+                                               break groupPrefixes;
+                                       }
+                               }
+                               if (authorizable == null)
+                                       throw new ArgeoException("Authorizable " + principalName
+                                                       + " not found");
+                       }
+                       return authorizable.getPrincipal();
+               }
+       }
+
+       public void setGroupPrefixes(List<String> groupsToCreate) {
+               this.groupPrefixes = groupsToCreate;
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitContainer.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitContainer.java
new file mode 100644 (file)
index 0000000..9060b58
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * 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.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.MaintainedRepository;
+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}.
+ */
+public class JackrabbitContainer extends JackrabbitWrapper implements
+               MaintainedRepository {
+       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>();
+
+       /**
+        * 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 ArgeoException(
+                                       "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();
+                       configurationIn = readConfiguration();
+                       vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
+                                       getHomeDirectory().getCanonicalPath());
+                       repositoryConfig = RepositoryConfig.create(new InputSource(
+                                       configurationIn), 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 ArgeoException("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 ArgeoException("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 (ArgeoException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new ArgeoException("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 ArgeoException("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;
+                       log.info("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 ArgeoException("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 ArgeoException("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() {
+               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 ArgeoException("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 ArgeoException("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;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitDataModelMigration.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitDataModelMigration.java
new file mode 100644 (file)
index 0000000..401b34d
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * 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.ArgeoException;
+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 ArgeoException("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";
+                       repositoryConfig.getFileSystem().deleteFile(customeNodeTypesPath);
+                       if (log.isDebugEnabled())
+                               log.debug("Cleared " + customeNodeTypesPath);
+               } catch (Exception e) {
+                       throw new ArgeoException("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/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitRepositoryFactory.java
new file mode 100644 (file)
index 0000000..d64bb5e
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * 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.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.ArgeoException;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.DefaultRepositoryFactory;
+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 extends DefaultRepositoryFactory
+               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", "unchecked" })
+       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
+               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));
+                       }
+               }
+
+               // publish under alias
+               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);
+               }
+
+               return repository;
+       }
+
+       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 ArgeoException("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 ArgeoException("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 ArgeoException("Cannot create repository " + uri, e);
+               } finally {
+                       IOUtils.closeQuietly(configurationIn);
+               }
+       }
+
+       /**
+        * 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/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitWrapper.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitWrapper.java
new file mode 100644 (file)
index 0000000..f9f04c4
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * 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.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Repository;
+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.commons.NamespaceHelper;
+import org.apache.jackrabbit.commons.cnd.CndImporter;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoJcrConstants;
+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}.
+ */
+public class JackrabbitWrapper extends JcrRepositoryWrapper implements
+               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 ArgeoException("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.isDebugEnabled())
+                                                       log.debug("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 ArgeoException("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 ArgeoException("No " + resUrl + " in the classpath,"
+                                               + " make sure the containing" + " package is visible.");
+                       }
+
+                       return IOUtils.toByteArray(in);
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot read CND from " + resUrl, e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+               }
+       }
+
+       /*
+        * REPOSITORY INTERCEPTOR
+        */
+
+       /*
+        * 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 paSr = bundleContext
+                               .getServiceReference(PackageAdmin.class.getName());
+               PackageAdmin packageAdmin = (PackageAdmin) bundleContext
+                               .getService(paSr);
+
+               // find exported package
+               ExportedPackage exportedPackage = null;
+               ExportedPackage[] exportedPackages = packageAdmin
+                               .getExportedPackages(pkg);
+               if (exportedPackages == null)
+                       throw new ArgeoException("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 ArgeoException("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;
+       }
+
+       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/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/OsgiJackrabbitRepositoryFactory.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/OsgiJackrabbitRepositoryFactory.java
new file mode 100644 (file)
index 0000000..b28699e
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.util.Hashtable;
+import java.util.Properties;
+
+import javax.jcr.Repository;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * OSGi-aware Jackrabbit repository factory which can retrieve/publish
+ * {@link Repository} as OSGi services.
+ */
+public class OsgiJackrabbitRepositoryFactory extends
+               JackrabbitRepositoryFactory {
+       private BundleContext bundleContext;
+
+       protected void publish(String alias, Repository repository,
+                       Properties properties) {
+               if (bundleContext != null) {
+                       // do not modify reference
+                       Hashtable<String, String> props = new Hashtable<String, String>();
+                       props.putAll(props);
+                       props.put(JCR_REPOSITORY_ALIAS, alias);
+                       bundleContext.registerService(Repository.class.getName(),
+                                       repository, props);
+               }
+       }
+
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/AbstractJackrabbitHandlerMapping.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/AbstractJackrabbitHandlerMapping.java
new file mode 100644 (file)
index 0000000..99bfab4
--- /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.jackrabbit.remote;
+
+import org.apache.jackrabbit.server.SessionProvider;
+import org.argeo.jcr.mvc.MultipleRepositoryHandlerMapping;
+
+/** Base class for Jackrabbit handler mappings. */
+public abstract class AbstractJackrabbitHandlerMapping extends
+               MultipleRepositoryHandlerMapping {
+       private SessionProvider sessionProvider;
+
+       protected SessionProvider getSessionProvider() {
+               return sessionProvider;
+       }
+
+       public void setSessionProvider(SessionProvider sessionProvider) {
+               this.sessionProvider = sessionProvider;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/ExtendedDispatcherServlet.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/ExtendedDispatcherServlet.java
new file mode 100644 (file)
index 0000000..14b6e99
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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.remote;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.springframework.web.servlet.DispatcherServlet;
+
+/**
+ * Overrides Spring {@link DispatcherServlet}, see
+ * http://forum.springsource.org/showthread.php?t=53472.
+ */
+public class ExtendedDispatcherServlet extends DispatcherServlet {
+       private static final long serialVersionUID = -5584673209855752009L;
+
+       private final static Log log = LogFactory
+                       .getLog(ExtendedDispatcherServlet.class);
+
+       protected void service(HttpServletRequest request,
+                       HttpServletResponse response) throws ServletException,
+                       java.io.IOException {
+               try {
+                       if (log.isTraceEnabled()) {
+                               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"));
+                       }
+                       doService(request, response);
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot process request", e);
+               }
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/IOHandlerWrapper.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/IOHandlerWrapper.java
new file mode 100644 (file)
index 0000000..daf2ecb
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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.remote;
+
+import java.io.IOException;
+
+import org.apache.jackrabbit.server.io.ExportContext;
+import org.apache.jackrabbit.server.io.IOHandler;
+import org.apache.jackrabbit.server.io.IOManager;
+import org.apache.jackrabbit.server.io.ImportContext;
+import org.apache.jackrabbit.webdav.DavResource;
+import org.argeo.ArgeoException;
+
+/** Wraps an IOHandler so that it can be injected a posteriori */
+public class IOHandlerWrapper implements IOHandler {
+       private IOManager ioManager = null;
+       private IOHandler ioHandler = null;
+
+       public void setIOHandler(IOHandler ioHandler) {
+               if ((this.ioHandler != null) && (ioHandler != null))
+                       throw new ArgeoException(
+                                       "There is already an IO Handler registered");
+               this.ioHandler = ioHandler;
+               if (ioManager != null && this.ioHandler != null)
+                       ioHandler.setIOManager(ioManager);
+       }
+
+       public IOHandler getIOHandler() {
+               return ioHandler;
+       }
+
+       public IOManager getIOManager() {
+               return ioManager;
+       }
+
+       public void setIOManager(IOManager ioManager) {
+               this.ioManager = ioManager;
+               if (ioHandler != null)
+                       ioHandler.setIOManager(ioManager);
+       }
+
+       public String getName() {
+               if (ioHandler != null)
+                       return ioHandler.getName();
+               else
+                       return "Empty IOHandler Wrapper";
+       }
+
+       public boolean canImport(ImportContext context, boolean isCollection) {
+               if (ioHandler != null)
+                       return ioHandler.canImport(context, isCollection);
+               return false;
+       }
+
+       public boolean canImport(ImportContext context, DavResource resource) {
+               if (ioHandler != null)
+                       return ioHandler.canImport(context, resource);
+               return false;
+       }
+
+       public boolean importContent(ImportContext context, boolean isCollection)
+                       throws IOException {
+               if (ioHandler != null)
+                       ioHandler.importContent(context, isCollection);
+               return false;
+       }
+
+       public boolean importContent(ImportContext context, DavResource resource)
+                       throws IOException {
+               if (ioHandler != null)
+                       ioHandler.importContent(context, resource);
+               return false;
+       }
+
+       public boolean canExport(ExportContext context, boolean isCollection) {
+               if (ioHandler != null)
+                       ioHandler.canExport(context, isCollection);
+               return false;
+       }
+
+       public boolean canExport(ExportContext context, DavResource resource) {
+               if (ioHandler != null)
+                       ioHandler.canExport(context, resource);
+               return false;
+       }
+
+       public boolean exportContent(ExportContext context, boolean isCollection)
+                       throws IOException {
+               if (ioHandler != null)
+                       ioHandler.exportContent(context, isCollection);
+               return false;
+       }
+
+       public boolean exportContent(ExportContext context, DavResource resource)
+                       throws IOException {
+               if (ioHandler != null)
+                       ioHandler.exportContent(context, resource);
+               return false;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/IOManagerBean.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/IOManagerBean.java
new file mode 100644 (file)
index 0000000..82a7673
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.remote;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.jackrabbit.server.io.ExportContext;
+import org.apache.jackrabbit.server.io.IOHandler;
+import org.apache.jackrabbit.server.io.IOManager;
+import org.apache.jackrabbit.server.io.ImportContext;
+import org.apache.jackrabbit.webdav.DavResource;
+import org.apache.tika.detect.Detector;
+
+/** {@link IOManager} that can easily be configured as a bean. */
+public class IOManagerBean implements IOManager {
+       private Detector detector = null;
+       private List<IOHandler> ioHandlers = new ArrayList<IOHandler>();
+
+       public boolean importContent(ImportContext context, boolean isCollection)
+                       throws IOException {
+               // TODO Auto-generated method stub
+               return false;
+       }
+
+       public boolean importContent(ImportContext context, DavResource resource)
+                       throws IOException {
+               // TODO Auto-generated method stub
+               return false;
+       }
+
+       public boolean exportContent(ExportContext context, boolean isCollection)
+                       throws IOException {
+               // TODO Auto-generated method stub
+               return false;
+       }
+
+       public boolean exportContent(ExportContext context, DavResource resource)
+                       throws IOException {
+               // TODO Auto-generated method stub
+               return false;
+       }
+
+       public synchronized void addIOHandler(IOHandler ioHandler) {
+               ioHandlers.add(ioHandler);
+       }
+
+       public synchronized IOHandler[] getIOHandlers() {
+               return ioHandlers.toArray(new IOHandler[ioHandlers.size()]);
+       }
+
+       public Detector getDetector() {
+               return detector;
+       }
+
+       public void setDetector(Detector detector) {
+               this.detector = detector;
+       }
+
+       public synchronized List<IOHandler> getIoHandlers() {
+               return ioHandlers;
+       }
+
+       public synchronized void setIoHandlers(List<IOHandler> ioHandlers) {
+               this.ioHandlers = ioHandlers;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/JcrRemotingHandlerMapping.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/JcrRemotingHandlerMapping.java
new file mode 100644 (file)
index 0000000..caea5fc
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.remote;
+
+import java.util.Properties;
+
+import javax.jcr.Repository;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+
+public class JcrRemotingHandlerMapping extends AbstractJackrabbitHandlerMapping {
+
+       protected HttpServlet createServlet(Repository repository, String pathPrefix)
+                       throws ServletException {
+               JcrRemotingServlet servlet = new JcrRemotingServlet(repository,
+                               getSessionProvider());
+               Properties initParameters = new Properties();
+               initParameters.setProperty(
+                               JcrRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, pathPrefix);
+               servlet.init(new DelegatingServletConfig(pathPrefix.replace('/', '_'),
+                               initParameters));
+               return servlet;
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/JcrRemotingServlet.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/JcrRemotingServlet.java
new file mode 100644 (file)
index 0000000..b3f0797
--- /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.jackrabbit.remote;
+
+import javax.jcr.Repository;
+
+import org.apache.jackrabbit.server.SessionProvider;
+
+public class JcrRemotingServlet extends
+               org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet {
+
+       private static final long serialVersionUID = 3131835511468341309L;
+
+       private final Repository repository;
+       private final SessionProvider sessionProvider;
+
+       public JcrRemotingServlet(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/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/OpenInViewSessionProvider.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/OpenInViewSessionProvider.java
new file mode 100644 (file)
index 0000000..b4d1d60
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.remote;
+
+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
+ */
+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/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/ScopedSessionProvider.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/ScopedSessionProvider.java
new file mode 100644 (file)
index 0000000..ffe6df9
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * 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.remote;
+
+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 javax.servlet.http.HttpSession;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.server.SessionProvider;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.JcrUtils;
+import org.springframework.security.Authentication;
+import org.springframework.security.context.SecurityContextHolder;
+
+/**
+ * Session provider assuming a single workspace and a short life cycle,
+ * typically a Spring bean of scope (web) 'session'.
+ */
+public class ScopedSessionProvider implements SessionProvider, Serializable {
+       private static final long serialVersionUID = 6589775984177317058L;
+       private static final Log log = LogFactory
+                       .getLog(ScopedSessionProvider.class);
+       private transient HttpSession httpSession = null;
+       private transient Session jcrSession = null;
+
+       private transient String currentRepositoryName = null;
+       private transient String currentWorkspaceName = null;
+       private transient String currentJcrUser = null;
+
+       // private transient String anonymousUserId = "anonymous";
+
+       public Session getSession(HttpServletRequest request, Repository rep,
+                       String workspace) throws LoginException, ServletException,
+                       RepositoryException {
+
+               Authentication authentication = SecurityContextHolder.getContext()
+                               .getAuthentication();
+               if (authentication == null)
+                       throw new ArgeoException(
+                                       "Request not authenticated by Spring Security");
+               String springUser = authentication.getName();
+
+               // HTTP
+               String requestJcrRepository = (String) request
+                               .getAttribute(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS);
+
+               // HTTP session
+               if (httpSession != null
+                               && !httpSession.getId().equals(request.getSession().getId()))
+                       throw new ArgeoException(
+                                       "Only session scope is supported in this mode");
+               if (httpSession == null)
+                       httpSession = request.getSession();
+
+               // Initializes current values
+               if (currentRepositoryName == null)
+                       currentRepositoryName = requestJcrRepository;
+               if (currentWorkspaceName == null)
+                       currentWorkspaceName = workspace;
+               if (currentJcrUser == null)
+                       currentJcrUser = springUser;
+
+               // logout if there was a change in session coordinates
+               if (jcrSession != null)
+                       if (!currentRepositoryName.equals(requestJcrRepository)) {
+                               if (log.isDebugEnabled())
+                                       log.debug(getHttpSessionId() + " Changed from repository '"
+                                                       + currentRepositoryName + "' to '"
+                                                       + requestJcrRepository
+                                                       + "', logging out cached JCR session.");
+                               logout();
+                       } else if (!currentWorkspaceName.equals(workspace)) {
+                               if (log.isDebugEnabled())
+                                       log.debug(getHttpSessionId() + " Changed from workspace '"
+                                                       + currentWorkspaceName + "' to '" + workspace
+                                                       + "', logging out cached JCR session.");
+                               logout();
+                       } else if (!currentJcrUser.equals(springUser)) {
+                               if (log.isDebugEnabled())
+                                       log.debug(getHttpSessionId() + " Changed from user '"
+                                                       + currentJcrUser + "' to '" + springUser
+                                                       + "', logging out cached JCR session.");
+                               logout();
+                       }
+
+               // login if needed
+               if (jcrSession == null)
+                       try {
+                               Session session = login(rep, workspace);
+                               if (!session.getUserID().equals(springUser)) {
+                                       JcrUtils.logoutQuietly(session);
+                                       throw new ArgeoException("Spring Security user '"
+                                                       + springUser + "' not in line with JCR user '"
+                                                       + session.getUserID() + "'");
+                               }
+                               currentRepositoryName = requestJcrRepository;
+                               // do not use workspace variable which may be null
+                               currentWorkspaceName = session.getWorkspace().getName();
+                               currentJcrUser = session.getUserID();
+
+                               jcrSession = session;
+                               return jcrSession;
+                       } catch (RepositoryException e) {
+                               throw new ArgeoException("Cannot open session to workspace "
+                                               + workspace, e);
+                       }
+
+               // returns cached session
+               return jcrSession;
+       }
+
+       protected Session login(Repository repository, String workspace)
+                       throws RepositoryException {
+               Session session = repository.login(workspace);
+               if (log.isDebugEnabled())
+                       log.debug(getHttpSessionId() + " User '" + session.getUserID()
+                                       + "' logged in workspace '"
+                                       + session.getWorkspace().getName() + "' of repository '"
+                                       + currentRepositoryName + "'");
+               return session;
+       }
+
+       public void releaseSession(Session session) {
+               if (log.isTraceEnabled())
+                       log.trace(getHttpSessionId() + " Releasing JCR session " + session);
+       }
+
+       protected void logout() {
+               JcrUtils.logoutQuietly(jcrSession);
+               jcrSession = null;
+       }
+
+       protected final String getHttpSessionId() {
+               return httpSession != null ? httpSession.getId() : "<null>";
+       }
+
+       public void init() {
+       }
+
+       public void destroy() {
+               logout();
+               if (getHttpSessionId() != null)
+                       if (log.isDebugEnabled())
+                               log.debug(getHttpSessionId()
+                                               + " Cleaned up provider for web session ");
+               httpSession = null;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleSessionProvider.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleSessionProvider.java
new file mode 100644 (file)
index 0000000..1d438d5
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * 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.remote;
+
+import java.io.Serializable;
+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.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+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.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.server.SessionProvider;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+
+/**
+ * Implements an open session in view patter: a new JCR session is created for
+ * each request
+ * 
+ * @deprecated use {@link ScopedSessionProvider} or
+ *             {@link OpenInViewSessionProvider}
+ */
+@Deprecated
+public class SimpleSessionProvider implements SessionProvider, Serializable {
+       private static final long serialVersionUID = 2270957712453841368L;
+
+       private final static Log log = LogFactory
+                       .getLog(SimpleSessionProvider.class);
+
+       private transient Map<String, Session> sessions;
+
+       private Boolean openSessionInView = true;
+
+       private String defaultWorkspace = "default";
+
+       private String webSessionId = null;
+
+       public Session getSession(HttpServletRequest request, Repository rep,
+                       String workspace) throws LoginException, ServletException,
+                       RepositoryException {
+
+               if (openSessionInView) {
+                       JackrabbitSession session = (JackrabbitSession) login(request, rep,
+                                       workspace);
+                       if (session.getWorkspace().getName().equals(defaultWorkspace))
+                               writeRemoteRoles(session);
+                       return session;
+               } else {
+                       if (webSessionId != null
+                                       && !webSessionId.equals(request.getSession().getId()))
+                               throw new ArgeoException(
+                                               "Only session scope is supported in this mode");
+                       webSessionId = request.getSession().getId();
+
+                       // since sessions is transient it can't be restored from the session
+                       if (sessions == null)
+                               sessions = Collections
+                                               .synchronizedMap(new HashMap<String, Session>());
+
+                       if (!sessions.containsKey(workspace)) {
+                               try {
+                                       // JackrabbitSession session = (JackrabbitSession)
+                                       // rep.login(
+                                       // null, workspace);
+                                       JackrabbitSession session = (JackrabbitSession) login(
+                                                       request, rep, workspace);
+                                       if (session.getWorkspace().getName()
+                                                       .equals(defaultWorkspace))
+                                               writeRemoteRoles(session);
+                                       if (log.isTraceEnabled())
+                                               log.trace("User " + session.getUserID()
+                                                               + " logged into " + request.getServletPath());
+                                       sessions.put(workspace, session);
+                                       return session;
+                               } catch (Exception e) {
+                                       throw new ArgeoException("Cannot open session", e);
+                               }
+                       } else {
+                               Session session = sessions.get(workspace);
+                               if (!session.isLive()) {
+                                       sessions.remove(workspace);
+                                       session = login(request, rep, workspace);
+                                       sessions.put(workspace, session);
+                               }
+                               return session;
+                       }
+               }
+       }
+
+       protected Session login(HttpServletRequest request, Repository repository,
+                       String workspace) throws RepositoryException {
+               if (log.isDebugEnabled())
+                       log.debug("Login to workspace "
+                                       + (workspace == null ? "<default>" : workspace)
+                                       + " in web session " + request.getSession().getId());
+               return repository.login(workspace);
+       }
+
+       protected void writeRemoteRoles(JackrabbitSession session)
+                       throws RepositoryException {
+               // FIXME better deal w/ non node repo
+
+               // retrieve roles
+               String userId = session.getUserID();
+               UserManager userManager = session.getUserManager();
+               User user = (User) userManager.getAuthorizable(userId);
+               if (user == null) {
+                       // anonymous
+                       return;
+               }
+               List<String> userGroupIds = new ArrayList<String>();
+               if (user != null)
+                       for (Iterator<Group> it = user.memberOf(); it.hasNext();)
+                               userGroupIds.add(it.next().getID());
+
+               // write roles if needed
+               Node userHome = UserJcrUtils.getUserHome(session);
+               boolean writeRoles = false;
+               if (userHome.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
+                       Value[] roles = userHome.getProperty(ArgeoNames.ARGEO_REMOTE_ROLES)
+                                       .getValues();
+                       if (roles.length != userGroupIds.size())
+                               writeRoles = true;
+                       else
+                               for (int i = 0; i < roles.length; i++)
+                                       if (!roles[i].getString().equals(userGroupIds.get(i)))
+                                               writeRoles = true;
+               } else
+                       writeRoles = true;
+
+               if (writeRoles) {
+                       session.getWorkspace().getVersionManager()
+                                       .checkout(userHome.getPath());
+                       String[] roleIds = userGroupIds.toArray(new String[userGroupIds
+                                       .size()]);
+                       userHome.setProperty(ArgeoNames.ARGEO_REMOTE_ROLES, roleIds);
+                       JcrUtils.updateLastModified(userHome);
+                       session.save();
+                       session.getWorkspace().getVersionManager()
+                                       .checkin(userHome.getPath());
+               }
+
+       }
+
+       public void releaseSession(Session session) {
+               if (log.isTraceEnabled())
+                       log.trace("Releasing JCR session " + session);
+               if (openSessionInView) {
+                       JcrUtils.logoutQuietly(session);
+                       if (log.isDebugEnabled())
+                               log.debug("Logged out remote JCR session " + session);
+               }
+       }
+
+       public void init() {
+               if (log.isDebugEnabled())
+                       log.debug("Init session provider for web session " + webSessionId);
+       }
+
+       public void destroy() {
+               if (log.isDebugEnabled())
+                       log.debug("Destroy session provider for web session "
+                                       + webSessionId);
+
+               if (sessions != null)
+                       for (String workspace : sessions.keySet()) {
+                               Session session = sessions.get(workspace);
+                               JcrUtils.logoutQuietly(session);
+                       }
+       }
+
+       /**
+        * If set to true a new session will be created each time (the default),
+        * otherwise a single session is cached by workspace and the object should
+        * be of scope session (not supported)
+        */
+       public void setOpenSessionInView(Boolean openSessionInView) {
+               this.openSessionInView = openSessionInView;
+       }
+
+       public void setSecurityWorkspace(String securityWorkspace) {
+               this.defaultWorkspace = securityWorkspace;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleWebdavHandlerMapping.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleWebdavHandlerMapping.java
new file mode 100644 (file)
index 0000000..bd8d804
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.remote;
+
+import java.util.Properties;
+
+import javax.jcr.Repository;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+
+/** Handler mapping for WebDav */
+public class SimpleWebdavHandlerMapping extends
+               AbstractJackrabbitHandlerMapping {
+       private String configuration;
+
+       protected HttpServlet createServlet(Repository repository, String pathPrefix)
+                       throws ServletException {
+
+               SimpleWebdavServlet servlet = new SimpleWebdavServlet(repository,
+                               getSessionProvider());
+               Properties initParameters = new Properties();
+               initParameters.setProperty(
+                               SimpleWebdavServlet.INIT_PARAM_RESOURCE_CONFIG, configuration);
+               initParameters
+                               .setProperty(
+                                               SimpleWebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX,
+                                               pathPrefix);
+               servlet.init(new DelegatingServletConfig(pathPrefix.replace('/', '_'),
+                               initParameters));
+               return servlet;
+       }
+
+       public void setConfiguration(String configuration) {
+               this.configuration = configuration;
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleWebdavServlet.java b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleWebdavServlet.java
new file mode 100644 (file)
index 0000000..f1ca0a9
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.remote;
+
+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;
+
+/** WebDav servlet whose repository is injected */
+public class SimpleWebdavServlet extends
+               org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet {
+       private static final long serialVersionUID = -369787931175177080L;
+
+       private final static Log log = LogFactory.getLog(SimpleWebdavServlet.class);
+
+       private final Repository repository;
+
+       public SimpleWebdavServlet(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/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/resources/org/argeo/jackrabbit/repository-fs.xml b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/resources/org/argeo/jackrabbit/repository-fs.xml
new file mode 100644 (file)
index 0000000..609fc8b
--- /dev/null
@@ -0,0 +1,53 @@
+<?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>
+       <!-- File system and datastore -->
+       <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+               <param name="path" value="${rep.home}/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="${argeo.node.repo.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" />
+               <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.local.LocalFileSystem">
+                       <param name="path" value="${rep.home}/version" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager" />
+       </Versioning>
+
+       <!-- Indexing -->
+       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+               <param name="path" value="${rep.home}/index" />
+               <param name="extractorPoolSize" value="2" />
+               <param name="supportHighlighting" value="true" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security">
+               </SecurityManager>
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
+               </AccessManager>
+               <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
+               </LoginModule>
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/resources/org/argeo/jackrabbit/repository-h2.xml b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/resources/org/argeo/jackrabbit/repository-h2.xml
new file mode 100644 (file)
index 0000000..b6a9252
--- /dev/null
@@ -0,0 +1,77 @@
+<?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:${rep.home}/h2/repository" />
+                       <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.FileDataStore">
+               <param name="path" value="${rep.home}/datastore" />
+       </DataStore>
+
+       <!-- Workspace templates -->
+       <Workspaces rootPath="${rep.home}/workspaces"
+               defaultWorkspace="main" />
+       <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}/index" />
+               <param name="extractorPoolSize" value="2" />
+               <param name="supportHighlighting" value="true" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security">
+               </SecurityManager>
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
+               </AccessManager>
+               <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
+               </LoginModule>
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/resources/org/argeo/jackrabbit/repository-memory.xml b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/resources/org/argeo/jackrabbit/repository-memory.xml
new file mode 100644 (file)
index 0000000..e552c33
--- /dev/null
@@ -0,0 +1,69 @@
+<?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">
+               </SecurityManager>
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
+               </AccessManager>
+               <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
+               </LoginModule>
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/wikitext/design.mediawiki b/trunk/server/runtime/org.argeo.server.jackrabbit/src/main/wikitext/design.mediawiki
new file mode 100644 (file)
index 0000000..1a27383
--- /dev/null
@@ -0,0 +1,9 @@
+= Titre =\r
+\r
+ == Sous Titre ==\r
+\r
+* point1\r
+* point2\r
+\r
+ Code (il suffit de mettre un espace en début de ligne\r
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.jcr.mvc/.classpath b/trunk/server/runtime/org.argeo.server.jcr.mvc/.classpath
new file mode 100644 (file)
index 0000000..c5931a0
--- /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.6"/>>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/server/runtime/org.argeo.server.jcr.mvc/.project b/trunk/server/runtime/org.argeo.server.jcr.mvc/.project
new file mode 100644 (file)
index 0000000..73c19d5
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.jcr.mvc</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/trunk/server/runtime/org.argeo.server.jcr.mvc/build.properties b/trunk/server/runtime/org.argeo.server.jcr.mvc/build.properties
new file mode 100644 (file)
index 0000000..5fc538b
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .
diff --git a/trunk/server/runtime/org.argeo.server.jcr.mvc/pom.xml b/trunk/server/runtime/org.argeo.server.jcr.mvc/pom.xml
new file mode 100644 (file)
index 0000000..22ce0fa
--- /dev/null
@@ -0,0 +1,84 @@
+<?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.server</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.jcr.mvc</artifactId>
+       <name>Commons Server JCR MVC</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>
+                                                       org.argeo.jcr.mvc.*
+                                               </Export-Package>
+                                               <Import-Package>
+                                                       org.springframework.core,
+                                                       org.springframework.beans.factory,
+                                                       javax.xml.transform.*;version="0.0.0",
+                                                       javax.xml.parsers.*;version="0.0.0",
+                                                       org.w3c.dom.*;version="0.0.0",
+                                                       org.xml.sax.*;version="0.0.0",
+                                                       *
+                                               </Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.jcr</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.context</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.core</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.beans</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.web</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.web.servlet</artifactId>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/MultipleRepositoryHandlerMapping.java b/trunk/server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/MultipleRepositoryHandlerMapping.java
new file mode 100644 (file)
index 0000000..fb3f1ae
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * 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.mvc;
+
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryFactory;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.ArgeoJcrUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.web.context.ServletContextAware;
+import org.springframework.web.servlet.HandlerExecutionChain;
+import org.springframework.web.servlet.HandlerMapping;
+
+/** Handles multiple JCR servers with a single servlet. */
+public abstract class MultipleRepositoryHandlerMapping implements
+               HandlerMapping, ApplicationContextAware, ServletContextAware {
+       private final static Log log = LogFactory
+                       .getLog(MultipleRepositoryHandlerMapping.class);
+
+       private final static String MKCOL = "MKCOL";
+
+       private ConfigurableApplicationContext applicationContext;
+       private ServletContext servletContext;
+
+       // private RepositoryRegister repositoryRegister;
+       private RepositoryFactory repositoryFactory;
+
+       /** Actually creates the servlet to be registered. */
+       protected abstract HttpServlet createServlet(Repository repository,
+                       String pathPrefix) throws ServletException;
+
+       public HandlerExecutionChain getHandler(HttpServletRequest request)
+                       throws Exception {
+               if (log.isTraceEnabled()) {
+                       log.trace("getContextPath=" + request.getContextPath());
+                       log.trace("getServletPath=" + request.getServletPath());
+                       log.trace("getPathInfo=" + request.getPathInfo());
+               }
+
+               String pathInfo = request.getPathInfo();
+               String repositoryAlias = extractRepositoryAlias(pathInfo);
+               if (repositoryAlias.equals(""))
+                       return null;
+
+               // MKCOL on repository or root node doesn't make sense
+               // and causes issues
+               if (request.getMethod().equals(MKCOL)) {
+                       StringTokenizer st = new StringTokenizer(pathInfo, "/");
+                       if (!st.hasMoreTokens())
+                               return null;
+                       st.nextToken();// repository
+                       if (!st.hasMoreTokens())
+                               return null;
+                       st.nextToken();// workspace
+                       if (!st.hasMoreTokens())
+                               return null;
+               }
+
+               request.setAttribute(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS,
+                               repositoryAlias);
+               String pathPrefix = request.getServletPath() + '/' + repositoryAlias;
+               String beanName = pathPrefix;
+
+               if (!applicationContext.containsBean(beanName)) {
+                       Repository repository = ArgeoJcrUtils.getRepositoryByAlias(
+                                       repositoryFactory, repositoryAlias);
+                       // Repository repository = repositoryRegister.getRepositories().get(
+                       // repositoryAlias);
+                       HttpServlet servlet = createServlet(repository, pathPrefix);
+                       applicationContext.getBeanFactory().registerSingleton(beanName,
+                                       servlet);
+                       // TODO: unregister it as well
+               }
+               HttpServlet remotingServlet = (HttpServlet) applicationContext
+                               .getBean(beanName);
+               HandlerExecutionChain hec = new HandlerExecutionChain(remotingServlet);
+               return hec;
+       }
+
+       /** Returns the first two token of the path */
+       // protected String[] extractPrefix(String pathInfo) {
+       // String[] res = new String[2];
+       // StringTokenizer st = new StringTokenizer(pathInfo, "/");
+       // if (st.hasMoreTokens())
+       // res[0] = st.nextToken();
+       // if (st.hasMoreTokens())
+       // res[1] = st.nextToken();
+       // return res;
+       // }
+
+       /** Returns the first token of the path */
+       protected String extractRepositoryAlias(String pathInfo) {
+               StringBuffer buf = new StringBuffer();
+               for (int i = 1; i < pathInfo.length(); i++) {
+                       char c = pathInfo.charAt(i);
+                       if (c == '/')
+                               break;
+                       buf.append(c);
+               }
+               return buf.toString();
+       }
+
+       /** The repository name is the first part of the path info */
+       // protected String extractRepositoryName(List<String> pathTokens) {
+       // StringBuffer currName = new StringBuffer("");
+       // for (String token : pathTokens) {
+       // currName.append(token);
+       // if (repositoryRegister.getRepositories().containsKey(
+       // currName.toString()))
+       // return currName.toString();
+       // currName.append('/');
+       // }
+       // throw new ArgeoException("No repository can be found for request "
+       // + pathTokens);
+       // }
+
+       public void setApplicationContext(ApplicationContext applicationContext)
+                       throws BeansException {
+               this.applicationContext = (ConfigurableApplicationContext) applicationContext;
+       }
+
+       public void setServletContext(ServletContext servletContext) {
+               this.servletContext = servletContext;
+       }
+
+       // public void setRepositoryRegister(RepositoryRegister repositoryRegister)
+       // {
+       // this.repositoryRegister = repositoryRegister;
+       // }
+
+       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
+               this.repositoryFactory = repositoryFactory;
+       }
+
+       protected class DelegatingServletConfig implements ServletConfig {
+               private String name;
+               private Properties initParameters;
+
+               public DelegatingServletConfig(String name, Properties initParameters) {
+                       super();
+                       this.name = name;
+                       this.initParameters = initParameters;
+               }
+
+               public String getServletName() {
+                       return name;
+               }
+
+               public ServletContext getServletContext() {
+                       return servletContext;
+               }
+
+               public String getInitParameter(String paramName) {
+                       return initParameters.getProperty(paramName);
+               }
+
+               @SuppressWarnings("rawtypes")
+               public Enumeration getInitParameterNames() {
+                       return initParameters.keys();
+               }
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/ResourceProxyServlet.java b/trunk/server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/ResourceProxyServlet.java
new file mode 100644 (file)
index 0000000..c821be0
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * 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.mvc;
+
+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.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.proxy.ResourceProxy;
+
+/** 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 ArgeoException("Cannot download " + node, e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+                       JcrUtils.closeQuietly(binary);
+               }
+       }
+
+       public void setProxy(ResourceProxy resourceProxy) {
+               this.proxy = resourceProxy;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/.classpath b/trunk/server/runtime/org.argeo.server.jcr/.classpath
new file mode 100644 (file)
index 0000000..d40e15c
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+       <classpathentry kind="src" output="target/classes" path="src/main/resources"/>
+       <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+       <classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>>>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/server/runtime/org.argeo.server.jcr/.project b/trunk/server/runtime/org.argeo.server.jcr/.project
new file mode 100644 (file)
index 0000000..977218c
--- /dev/null
@@ -0,0 +1,28 @@
+<?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/trunk/server/runtime/org.argeo.server.jcr/build.properties b/trunk/server/runtime/org.argeo.server.jcr/build.properties
new file mode 100644 (file)
index 0000000..d564e57
--- /dev/null
@@ -0,0 +1,22 @@
+source.. = src/main/java/,\
+           src/test/java/,\
+           src/test/resources/,\
+           src/main/resources/
+output.. = target/classes/,\
+           target/test-classes/
+bin.includes = META-INF/,\
+               .
+additional.bundles = slf4j.api,\
+                     slf4j.log4j,\
+                     org.apache.log4j,\
+                     org.apache.commons.collections,\
+                     edu.oswego.cs.dl.util.concurrent,\
+                     org.apache.lucene,\
+                     junit,\
+                     org.apache.xml.serializer,\
+                     org.apache.commons.dbcp,\
+                     org.apache.commons.pool,\
+                     org.apache.jackrabbit,\
+                     org.h2,\
+                     org.apache.tika
+
diff --git a/trunk/server/runtime/org.argeo.server.jcr/pom.xml b/trunk/server/runtime/org.argeo.server.jcr/pom.xml
new file mode 100644 (file)
index 0000000..8b97a49
--- /dev/null
@@ -0,0 +1,136 @@
+<?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.server</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.jcr</artifactId>
+       <name>Commons Server JCR</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>org.argeo.jcr.*</Export-Package>
+                                               <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,
+                                                       *</Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.argeo.maven.plugins</groupId>
+                               <artifactId>maven-argeo-osgi-plugin</artifactId>
+                               <configuration>
+                                       <useDependencies>true</useDependencies>
+                                       <onlyCheck>true</onlyCheck>
+                                       <argsToAppend>
+                                               <arg>-clean</arg>
+                                       </argsToAppend>
+                                       <systemProperties>
+                                               <!-- Make sure that no bundle will be started -->
+                                               <argeo.osgi.start>XXX</argeo.osgi.start>
+                                       </systemProperties>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.util</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- JCR -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.jcr</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.commons.io</artifactId>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.core</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.beans</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- OSGi -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.eclipse.osgi</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+
+
+               <!-- TEST -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>junit</artifactId>
+                       <optional>true</optional>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.jackrabbit</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.support.junit</artifactId>
+                       <version>2.1.11</version>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.log4j</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.osgi.boot</artifactId>
+                       <version>2.1.11</version>
+                       <scope>test</scope>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoJcrConstants.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoJcrConstants.java
new file mode 100644 (file)
index 0000000..b9b513a
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/** 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)
+       public final static String JCR_REPOSITORY_ALIAS = "argeo.jcr.repository.alias";
+       public final static String JCR_REPOSITORY_URI = "argeo.jcr.repository.uri";
+
+       // standard aliases
+       public final static String ALIAS_NODE = "node";
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoJcrUtils.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoJcrUtils.java
new file mode 100644 (file)
index 0000000..dccb06c
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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;
+
+import org.argeo.ArgeoException;
+
+/** 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 ArgeoException(
+                                       "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 ArgeoException(
+                                       "Unexpected exception when trying to retrieve repository with uri "
+                                                       + uri, e);
+               }
+       }
+
+       private ArgeoJcrUtils() {
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoNames.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoNames.java
new file mode 100644 (file)
index 0000000..6e3eca9
--- /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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoTypes.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/CollectionNodeIterator.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/DefaultJcrListener.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/DefaultJcrListener.java
new file mode 100644 (file)
index 0000000..253b305
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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;
+import org.argeo.ArgeoException;
+
+/** 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 ArgeoException("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 ArgeoException("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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/DefaultRepositoryFactory.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/DefaultRepositoryFactory.java
new file mode 100644 (file)
index 0000000..a7b30e7
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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;
+
+import org.argeo.ArgeoException;
+
+/**
+ * Simple implementation of {@link RepositoryFactory}, supporting OSGi aliases.
+ */
+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 ArgeoException("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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/DefaultRepositoryRegister.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/DefaultRepositoryRegister.java
new file mode 100644 (file)
index 0000000..f13c84e
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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;
+
+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) {
+               // TODO: also check bean name?
+               String alias;
+               if (properties == null || !properties.containsKey(JCR_REPOSITORY_ALIAS)) {
+                       log.warn("Cannot register a repository if no "
+                                       + JCR_REPOSITORY_ALIAS + " property is speecified.");
+                       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);
+               map.put(alias, repository);
+               repositories = Collections.unmodifiableMap(map);
+               setChanged();
+               notifyObservers(alias);
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrCallback.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrRepositoryWrapper.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrRepositoryWrapper.java
new file mode 100644 (file)
index 0000000..f993c2f
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * 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;
+
+import org.argeo.ArgeoException;
+
+/**
+ * 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 ArgeoException("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 ArgeoException("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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrResourceAdapter.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrResourceAdapter.java
new file mode 100644 (file)
index 0000000..0b1a98c
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * 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;
+import org.argeo.ArgeoException;
+
+/**
+ * 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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("Cannot update node " + path, e);
+               }
+       }
+
+       public List<Calendar> listVersions(String path) {
+               if (!versioning)
+                       throw new ArgeoException("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 ArgeoException("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 ArgeoException("Cannot retrieve " + path, e);
+               }
+       }
+
+       public synchronized InputStream retrieve(String path, Integer revision) {
+               if (!versioning)
+                       throw new ArgeoException("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 ArgeoException("Cannot retrieve version " + revision
+                                       + " of " + path, e);
+               }
+
+               throw new ArgeoException("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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUrlStreamHandler.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java
new file mode 100644 (file)
index 0000000..2176e75
--- /dev/null
@@ -0,0 +1,1590 @@
+/*
+ * 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.ArgeoException;
+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 ArgeoException
+        *             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 ArgeoException("Cannot execute query " + query, e);
+               }
+               Node node;
+               if (nodeIterator.hasNext())
+                       node = nodeIterator.nextNode();
+               else
+                       return null;
+
+               if (nodeIterator.hasNext())
+                       throw new ArgeoException("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 ArgeoException("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 ArgeoException("Root path '/' has no parent path");
+               if (path.charAt(0) != '/')
+                       throw new ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException(
+                                       "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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException(
+                                               "Session has pending changes, save them first.");
+                       Node node = mkdirs(session, path, type);
+                       session.save();
+                       return node;
+               } catch (RepositoryException e) {
+                       discardQuietly(session);
+                       throw new ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException(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 ArgeoException("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 ArgeoException(
+                                       "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 ArgeoException(
+                                       "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 ArgeoException("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);
+                       }
+               }
+       }
+
+       /*
+        * 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("resource")
+       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 ArgeoException(
+                                                       "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 ArgeoException(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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException(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 ArgeoException("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 ArgeoException("Cannot checksum file " + fileNode, e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+                       closeQuietly(data);
+               }
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/MaintainedRepository.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/MaintainedRepository.java
new file mode 100644 (file)
index 0000000..702d47a
--- /dev/null
@@ -0,0 +1,8 @@
+package org.argeo.jcr;
+
+import javax.jcr.Repository;
+
+/** Abstracts maintenance operations on a {@link Repository} */
+public interface MaintainedRepository extends Repository {
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/NodeMapper.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/NodeMapper.java
new file mode 100644 (file)
index 0000000..af792c3
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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.Session;
+
+public interface NodeMapper {
+       public Object load(Node node);
+
+       public void update(Node node, Object obj);
+
+       public Node save(Session session, String path, Object obj);
+       
+       public void setNodeMapperProvider(NodeMapperProvider nmp);
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/NodeMapperProvider.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/NodeMapperProvider.java
new file mode 100644 (file)
index 0000000..07e623b
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+/** Provides a node mapper relevant for this node. */
+public interface NodeMapperProvider {
+
+       /** 
+        * Node Mapper is chosen regarding the Jcr path of the node parameter 
+        * @param Node node
+        * @return the node mapper or null if no relevant node mapper can be found. */
+       public NodeMapper findNodeMapper(Node node);
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/PropertyDiff.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/PropertyDiff.java
new file mode 100644 (file)
index 0000000..bdd316f
--- /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 javax.jcr.Value;
+
+import org.argeo.ArgeoException;
+
+/** 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 ArgeoException(
+                                               "Reference and new values must be specified.");
+               } else if (type == ADDED) {
+                       if (referenceValue != null || newValue == null)
+                               throw new ArgeoException(
+                                               "New value and only it must be specified.");
+               } else if (type == REMOVED) {
+                       if (referenceValue == null || newValue != null)
+                               throw new ArgeoException(
+                                               "Reference value and only it must be specified.");
+               } else {
+                       throw new ArgeoException("Unkown diff type " + type);
+               }
+
+               if (relPath == null)
+                       throw new ArgeoException("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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/RepositoryRegister.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ThreadBoundJcrSessionFactory.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ThreadBoundJcrSessionFactory.java
new file mode 100644 (file)
index 0000000..193f22c
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * 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;
+import org.argeo.ArgeoException;
+
+/** Proxy JCR sessions and attach them to calling threads. */
+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 ArgeoException("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 ArgeoException("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 ArgeoException("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 {
+               monitoringThread = new MonitoringThread();
+               monitoringThread.start();
+       }
+
+       public synchronized 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 ArgeoException("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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/UserJcrUtils.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/UserJcrUtils.java
new file mode 100644 (file)
index 0000000..1758802
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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;
+
+import org.argeo.ArgeoException;
+
+/** Utilities related to the user home and properties based on Argeo JCR model. */
+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 ArgeoException("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 ArgeoException(
+                                       "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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/VersionDiff.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/AbstractUrlProxy.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/AbstractUrlProxy.java
new file mode 100644 (file)
index 0000000..8a66f31
--- /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.ArgeoException;
+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 ArgeoException("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 ArgeoException("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 ArgeoException("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 ArgeoException("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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/ResourceProxy.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/security/JcrAuthorizations.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/security/JcrAuthorizations.java
new file mode 100644 (file)
index 0000000..491f8a6
--- /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.ArgeoException;
+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 ArgeoException(
+                                       "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 ArgeoException("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 ArgeoException("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 ArgeoException("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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/spring/BeanNodeMapper.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/spring/BeanNodeMapper.java
new file mode 100644 (file)
index 0000000..9f70f5c
--- /dev/null
@@ -0,0 +1,649 @@
+/*
+ * 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 java.beans.PropertyDescriptor;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.jcr.Binary;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.NodeMapper;
+import org.argeo.jcr.NodeMapperProvider;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+
+public class BeanNodeMapper implements NodeMapper {
+       private final static Log log = LogFactory.getLog(BeanNodeMapper.class);
+
+       private final static String NODE_VALUE = "value";
+
+       // private String keyNode = "bean:key";
+       private String uuidProperty = "uuid";
+       private String classProperty = "class";
+
+       private Boolean versioning = false;
+       private Boolean strictUuidReference = false;
+
+       // TODO define a primaryNodeType Strategy
+       private String primaryNodeType = null;
+
+       private ClassLoader classLoader = getClass().getClassLoader();
+
+       private NodeMapperProvider nodeMapperProvider;
+
+       /**
+        * exposed method to retrieve a bean from a node
+        */
+       public Object load(Node node) {
+               try {
+                       if (nodeMapperProvider != null) {
+                               NodeMapper nodeMapper = nodeMapperProvider.findNodeMapper(node);
+                               if (nodeMapper != this) {
+                                       return nodeMapper.load(node);
+                               }
+                       }
+                       return nodeToBean(node);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot load object from node " + node, e);
+               }
+       }
+
+       /** Update an existing node with an object */
+       public void update(Node node, Object obj) {
+               try {
+                       if (nodeMapperProvider != null) {
+
+                               NodeMapper nodeMapper = nodeMapperProvider.findNodeMapper(node);
+                               if (nodeMapper != this) {
+                                       nodeMapper.update(node, obj);
+                               } else
+                                       beanToNode(createBeanWrapper(obj), node);
+                       } else
+                               beanToNode(createBeanWrapper(obj), node);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot update node " + node + " with "
+                                       + obj, e);
+               }
+       }
+
+       /**
+        * if no storage path is given; we use canonical path
+        * 
+        * @see this.storagePath()
+        */
+       public Node save(Session session, Object obj) {
+               return save(session, storagePath(obj), obj);
+       }
+
+       /**
+        * Create a new node to store an object. If the parentNode doesn't exist, it
+        * is created
+        * 
+        * the primaryNodeType may be initialized before
+        */
+       public Node save(Session session, String path, Object obj) {
+               try {
+                       final Node node;
+                       String parentPath = JcrUtils.parentPath(path);
+                       // find or create parent node
+                       Node parentNode;
+                       if (session.itemExists(path))
+                               parentNode = (Node) session.getItem(parentPath);
+                       else {
+                               parentNode = JcrUtils.mkdirs(session, parentPath, null, null,
+                                               versioning);
+                       }
+                       // create node
+
+                       if (primaryNodeType != null)
+                               node = parentNode.addNode(JcrUtils.lastPathElement(path),
+                                               primaryNodeType);
+                       else
+                               node = parentNode.addNode(JcrUtils.lastPathElement(path));
+
+                       // Check specific cases
+                       if (nodeMapperProvider != null) {
+                               NodeMapper nodeMapper = nodeMapperProvider.findNodeMapper(node);
+                               if (nodeMapper != this) {
+                                       nodeMapper.update(node, obj);
+                                       return node;
+                               }
+                       }
+                       update(node, obj);
+                       return node;
+               } catch (ArgeoException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot save or update " + obj + " under "
+                                       + path, e);
+               }
+       }
+
+       /**
+        * Parse the FQN of a class to string with '/' delimiters Prefix the
+        * returned string with "/objects/"
+        */
+       public String storagePath(Object obj) {
+               String clss = obj.getClass().getName();
+               StringBuffer buf = new StringBuffer("/objects/");
+               StringTokenizer st = new StringTokenizer(clss, ".");
+               while (st.hasMoreTokens()) {
+                       buf.append(st.nextToken()).append('/');
+               }
+               buf.append(obj.toString());
+               return buf.toString();
+       }
+
+       @SuppressWarnings("unchecked")
+       /** 
+        * Transforms a node into an object of the class defined by classProperty Property
+        */
+       protected Object nodeToBean(Node node) throws RepositoryException {
+               if (log.isTraceEnabled())
+                       log.trace("Load     " + node);
+
+               try {
+                       String clssName = node.getProperty(classProperty).getValue()
+                                       .getString();
+
+                       BeanWrapper beanWrapper = createBeanWrapper(loadClass(clssName));
+
+                       // process properties
+                       PropertyIterator propIt = node.getProperties();
+                       props: while (propIt.hasNext()) {
+                               Property prop = propIt.nextProperty();
+                               if (!beanWrapper.isWritableProperty(prop.getName()))
+                                       continue props;
+
+                               PropertyDescriptor pd = beanWrapper.getPropertyDescriptor(prop
+                                               .getName());
+                               Class<?> propClass = pd.getPropertyType();
+
+                               if (log.isTraceEnabled())
+                                       log.trace("Load " + prop + ", propClass=" + propClass
+                                                       + ", property descriptor=" + pd);
+
+                               // primitive list
+                               if (propClass != null && List.class.isAssignableFrom(propClass)) {
+                                       List<Object> lst = new ArrayList<Object>();
+                                       Class<?> valuesClass = classFromProperty(prop);
+                                       if (valuesClass != null)
+                                               for (Value value : prop.getValues()) {
+                                                       lst.add(asObject(value, valuesClass));
+                                               }
+                                       continue props;
+                               }
+
+                               // Case of other type of property accepted by jcr
+                               // Long, Double, String, Binary, Date, Boolean, Name
+                               Object value = asObject(prop.getValue(), pd.getPropertyType());
+                               if (value != null)
+                                       beanWrapper.setPropertyValue(prop.getName(), value);
+                       }
+
+                       // process children nodes
+                       NodeIterator nodeIt = node.getNodes();
+                       nodes: while (nodeIt.hasNext()) {
+                               Node childNode = nodeIt.nextNode();
+                               String name = childNode.getName();
+                               if (!beanWrapper.isWritableProperty(name))
+                                       continue nodes;
+
+                               PropertyDescriptor pd = beanWrapper.getPropertyDescriptor(name);
+                               Class<?> propClass = pd.getPropertyType();
+
+                               // objects list
+                               if (propClass != null && List.class.isAssignableFrom(propClass)) {
+                                       String lstClass = childNode.getProperty(classProperty)
+                                                       .getString();
+                                       List<Object> lst;
+                                       try {
+                                               lst = (List<Object>) loadClass(lstClass).newInstance();
+                                       } catch (Exception e) {
+                                               lst = new ArrayList<Object>();
+                                       }
+
+                                       if (childNode.hasNodes()) {
+                                               // Look for children nodes
+                                               NodeIterator valuesIt = childNode.getNodes();
+                                               while (valuesIt.hasNext()) {
+                                                       Node lstValueNode = valuesIt.nextNode();
+                                                       Object lstValue = nodeToBean(lstValueNode);
+                                                       lst.add(lstValue);
+                                               }
+                                       } else {
+                                               // look for a property with the same name which will
+                                               // provide
+                                               // primitives
+                                               Property childProp = childNode.getProperty(childNode
+                                                               .getName());
+                                               Class<?> valuesClass = classFromProperty(childProp);
+                                               if (valuesClass != null)
+                                                       if (childProp.getDefinition().isMultiple())
+                                                               for (Value value : childProp.getValues()) {
+                                                                       lst.add(asObject(value, valuesClass));
+                                                               }
+                                                       else
+                                                               lst.add(asObject(childProp.getValue(),
+                                                                               valuesClass));
+                                       }
+                                       beanWrapper.setPropertyValue(name, lst);
+                                       continue nodes;
+                               }
+
+                               // objects map
+                               if (propClass != null && Map.class.isAssignableFrom(propClass)) {
+                                       String mapClass = childNode.getProperty(classProperty)
+                                                       .getString();
+                                       Map<Object, Object> map;
+                                       try {
+                                               map = (Map<Object, Object>) loadClass(mapClass)
+                                                               .newInstance();
+                                       } catch (Exception e) {
+                                               map = new HashMap<Object, Object>();
+                                       }
+
+                                       // properties
+                                       PropertyIterator keysPropIt = childNode.getProperties();
+                                       keyProps: while (keysPropIt.hasNext()) {
+                                               Property keyProp = keysPropIt.nextProperty();
+                                               // FIXME: use property editor
+                                               String key = keyProp.getName();
+                                               if (classProperty.equals(key))
+                                                       continue keyProps;
+
+                                               Class<?> keyPropClass = classFromProperty(keyProp);
+                                               if (keyPropClass != null) {
+                                                       Object mapValue = asObject(keyProp.getValue(),
+                                                                       keyPropClass);
+                                                       map.put(key, mapValue);
+                                               }
+                                       }
+
+                                       // node
+                                       NodeIterator keysIt = childNode.getNodes();
+                                       while (keysIt.hasNext()) {
+                                               Node mapValueNode = keysIt.nextNode();
+                                               // FIXME: use property editor
+                                               Object key = mapValueNode.getName();
+
+                                               Object mapValue = nodeToBean(mapValueNode);
+
+                                               map.put(key, mapValue);
+                                       }
+                                       beanWrapper.setPropertyValue(name, map);
+                                       continue nodes;
+                               }
+
+                               // default
+                               Object value = nodeToBean(childNode);
+                               beanWrapper.setPropertyValue(name, value);
+
+                       }
+                       return beanWrapper.getWrappedInstance();
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot map node " + node, e);
+               }
+       }
+
+       /**
+        * Transforms an object to the specified jcr Node in order to persist it.
+        * 
+        * @param beanWrapper
+        * @param node
+        * @throws RepositoryException
+        */
+       protected void beanToNode(BeanWrapper beanWrapper, Node node)
+                       throws RepositoryException {
+               properties: for (PropertyDescriptor pd : beanWrapper
+                               .getPropertyDescriptors()) {
+                       String name = pd.getName();
+                       if (!beanWrapper.isReadableProperty(name))
+                               continue properties;// skip
+
+                       Object value = beanWrapper.getPropertyValue(name);
+                       if (value == null) {
+                               // remove values when updating
+                               if (node.hasProperty(name))
+                                       node.setProperty(name, (Value) null);
+                               if (node.hasNode(name))
+                                       node.getNode(name).remove();
+
+                               continue properties;
+                       }
+
+                       // if (uuidProperty != null && uuidProperty.equals(name)) {
+                       // // node.addMixin(ArgeoJcrConstants.MIX_REFERENCEABLE);
+                       // node.setProperty(ArgeoJcrConstants.JCR_UUID, value.toString());
+                       // continue properties;
+                       // }
+
+                       if ("class".equals(name)) {
+                               if (classProperty != null) {
+                                       node.setProperty(classProperty,
+                                                       ((Class<?>) value).getName());
+                                       // TODO: store a class hierarchy?
+                               }
+                               continue properties;
+                       }
+
+                       // Some bean reference other classes. We must deal with this case
+                       if (value instanceof Class<?>) {
+                               node.setProperty(name, ((Class<?>) value).getName());
+                               continue properties;
+                       }
+
+                       Value val = asValue(node.getSession(), value);
+                       if (val != null) {
+                               node.setProperty(name, val);
+                               continue properties;
+                       }
+
+                       if (value instanceof List<?>) {
+                               List<?> lst = (List<?>) value;
+                               addList(node, name, lst);
+                               continue properties;
+                       }
+
+                       if (value instanceof Map<?, ?>) {
+                               Map<?, ?> map = (Map<?, ?>) value;
+                               addMap(node, name, map);
+                               continue properties;
+                       }
+
+                       BeanWrapper child = createBeanWrapper(value);
+                       // TODO: delegate to another mapper
+
+                       // TODO: deal with references
+                       // Node childNode = findChildReference(session, child);
+                       // if (childNode != null) {
+                       // node.setProperty(name, childNode);
+                       // continue properties;
+                       // }
+
+                       // default case (recursive)
+                       if (node.hasNode(name)) {// update
+                               // TODO: optimize
+                               node.getNode(name).remove();
+                       }
+                       Node childNode = node.addNode(name);
+                       beanToNode(child, childNode);
+               }
+       }
+
+       /**
+        * Process specific case of list
+        * 
+        * @param node
+        * @param name
+        * @param lst
+        * @throws RepositoryException
+        */
+       protected void addList(Node node, String name, List<?> lst)
+                       throws RepositoryException {
+               if (node.hasNode(name)) {// update
+                       // TODO: optimize
+                       node.getNode(name).remove();
+               }
+
+               Node listNode = node.addNode(name);
+               listNode.setProperty(classProperty, lst.getClass().getName());
+               Value[] values = new Value[lst.size()];
+               boolean atLeastOneSet = false;
+               for (int i = 0; i < lst.size(); i++) {
+                       Object lstValue = lst.get(i);
+                       values[i] = asValue(node.getSession(), lstValue);
+                       if (values[i] != null) {
+                               atLeastOneSet = true;
+                       } else {
+                               Node childNode = findChildReference(node.getSession(),
+                                               createBeanWrapper(lstValue));
+                               if (childNode != null) {
+                                       values[i] = node.getSession().getValueFactory()
+                                                       .createValue(childNode);
+                                       atLeastOneSet = true;
+                               }
+                       }
+               }
+
+               // will be either properties or nodes, not both
+               if (!atLeastOneSet && lst.size() != 0) {
+                       for (Object lstValue : lst) {
+                               Node childNode = listNode.addNode(NODE_VALUE);
+                               beanToNode(createBeanWrapper(lstValue), childNode);
+                       }
+               } else {
+                       listNode.setProperty(name, values);
+               }
+       }
+
+       /**
+        * Process specific case of maps.
+        * 
+        * @param node
+        * @param name
+        * @param map
+        * @throws RepositoryException
+        */
+       protected void addMap(Node node, String name, Map<?, ?> map)
+                       throws RepositoryException {
+               if (node.hasNode(name)) {// update
+                       // TODO: optimize
+                       node.getNode(name).remove();
+               }
+
+               Node mapNode = node.addNode(name);
+               mapNode.setProperty(classProperty, map.getClass().getName());
+               for (Object key : map.keySet()) {
+                       Object mapValue = map.get(key);
+                       // PropertyEditor pe = beanWrapper.findCustomEditor(key.getClass(),
+                       // null);
+                       String keyStr;
+                       // if (pe == null) {
+                       if (key instanceof CharSequence)
+                               keyStr = key.toString();
+                       else
+                               throw new ArgeoException(
+                                               "Cannot find property editor for class "
+                                                               + key.getClass());
+                       // } else {
+                       // pe.setValue(key);
+                       // keyStr = pe.getAsText();
+                       // }
+                       // TODO: check string format
+
+                       Value mapVal = asValue(node.getSession(), mapValue);
+                       if (mapVal != null)
+                               mapNode.setProperty(keyStr, mapVal);
+                       else {
+                               Node entryNode = mapNode.addNode(keyStr);
+                               beanToNode(createBeanWrapper(mapValue), entryNode);
+                       }
+
+               }
+
+       }
+
+       protected BeanWrapper createBeanWrapper(Object obj) {
+               return new BeanWrapperImpl(obj);
+       }
+
+       protected BeanWrapper createBeanWrapper(Class<?> clss) {
+               return new BeanWrapperImpl(clss);
+       }
+
+       /** Returns null if value cannot be found */
+       protected Value asValue(Session session, Object value)
+                       throws RepositoryException {
+               ValueFactory valueFactory = session.getValueFactory();
+               if (value instanceof Integer)
+                       return valueFactory.createValue((Integer) value);
+               else if (value instanceof Long)
+                       return valueFactory.createValue((Long) value);
+               else if (value instanceof Float)
+                       return valueFactory.createValue((Float) value);
+               else if (value instanceof Double)
+                       return valueFactory.createValue((Double) value);
+               else if (value instanceof Boolean)
+                       return valueFactory.createValue((Boolean) value);
+               else if (value instanceof Calendar)
+                       return valueFactory.createValue((Calendar) value);
+               else if (value instanceof Date) {
+                       Calendar cal = new GregorianCalendar();
+                       cal.setTime((Date) value);
+                       return valueFactory.createValue(cal);
+               } else if (value instanceof CharSequence)
+                       return valueFactory.createValue(value.toString());
+               else if (value instanceof InputStream) {
+                       Binary binary = session.getValueFactory().createBinary(
+                                       (InputStream) value);
+                       return valueFactory.createValue(binary);
+               } else
+                       return null;
+       }
+
+       protected Class<?> classFromProperty(Property property)
+                       throws RepositoryException {
+               switch (property.getType()) {
+               case PropertyType.LONG:
+                       return Long.class;
+               case PropertyType.DOUBLE:
+                       return Double.class;
+               case PropertyType.STRING:
+                       return String.class;
+               case PropertyType.BOOLEAN:
+                       return Boolean.class;
+               case PropertyType.DATE:
+                       return Calendar.class;
+               case PropertyType.NAME:
+                       return null;
+               default:
+                       throw new ArgeoException("Cannot find class for property "
+                                       + property + ", type="
+                                       + PropertyType.nameFromValue(property.getType()));
+               }
+       }
+
+       protected Object asObject(Value value, Class<?> propClass)
+                       throws RepositoryException {
+               if (propClass.equals(Integer.class))
+                       return (int) value.getLong();
+               else if (propClass.equals(Long.class))
+                       return value.getLong();
+               else if (propClass.equals(Float.class))
+                       return (float) value.getDouble();
+               else if (propClass.equals(Double.class))
+                       return value.getDouble();
+               else if (propClass.equals(Boolean.class))
+                       return value.getBoolean();
+               else if (CharSequence.class.isAssignableFrom(propClass))
+                       return value.getString();
+               else if (InputStream.class.isAssignableFrom(propClass))
+                       return value.getBinary().getStream();
+               else if (Calendar.class.isAssignableFrom(propClass))
+                       return value.getDate();
+               else if (Date.class.isAssignableFrom(propClass))
+                       return value.getDate().getTime();
+               else
+                       return null;
+       }
+
+       protected Node findChildReference(Session session, BeanWrapper child)
+                       throws RepositoryException {
+               if (child.isReadableProperty(uuidProperty)) {
+                       String childUuid = child.getPropertyValue(uuidProperty).toString();
+                       try {
+                               return session.getNodeByIdentifier(childUuid);
+                       } catch (ItemNotFoundException e) {
+                               if (strictUuidReference)
+                                       throw new ArgeoException("No node found with uuid "
+                                                       + childUuid, e);
+                       }
+               }
+               return null;
+       }
+
+       protected Class<?> loadClass(String name) {
+               // log.debug("Class loader: " + classLoader);
+               try {
+                       return classLoader.loadClass(name);
+               } catch (ClassNotFoundException e) {
+                       throw new ArgeoException("Cannot load class " + name, e);
+               }
+       }
+
+       protected String propertyName(String name) {
+               return name;
+       }
+
+       public void setVersioning(Boolean versioning) {
+               this.versioning = versioning;
+       }
+
+       public void setUuidProperty(String uuidProperty) {
+               this.uuidProperty = uuidProperty;
+       }
+
+       public void setClassProperty(String classProperty) {
+               this.classProperty = classProperty;
+       }
+
+       public void setStrictUuidReference(Boolean strictUuidReference) {
+               this.strictUuidReference = strictUuidReference;
+       }
+
+       public void setPrimaryNodeType(String primaryNodeType) {
+               this.primaryNodeType = primaryNodeType;
+       }
+
+       public void setClassLoader(ClassLoader classLoader) {
+               this.classLoader = classLoader;
+       }
+
+       public void setNodeMapperProvider(NodeMapperProvider nodeMapperProvider) {
+               this.nodeMapperProvider = nodeMapperProvider;
+       }
+
+       public String getPrimaryNodeType() {
+               return this.primaryNodeType;
+       }
+
+       public String getClassProperty() {
+               return this.classProperty;
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/spring/ThreadBoundSession.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/spring/ThreadBoundSession.java
new file mode 100644 (file)
index 0000000..a46bef1
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+public class ThreadBoundSession extends ThreadBoundJcrSessionFactory implements FactoryBean, InitializingBean, DisposableBean{
+       public void afterPropertiesSet() throws Exception {
+               init();
+       }
+
+       public void destroy() throws Exception {
+               dispose();
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/tabular/JcrTabularRowIterator.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/tabular/JcrTabularRowIterator.java
new file mode 100644 (file)
index 0000000..ca0635c
--- /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.ArgeoException;
+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 ArgeoException("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/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/tabular/JcrTabularWriter.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/tabular/JcrTabularWriter.java
new file mode 100644 (file)
index 0000000..718ff23
--- /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.ArgeoException;
+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 ArgeoException("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 ArgeoException("Cannot store data in " + contentNode, e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+                       JcrUtils.closeQuietly(binary);
+               }
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/unit/AbstractJcrTestCase.java b/trunk/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/unit/AbstractJcrTestCase.java
new file mode 100644 (file)
index 0000000..530605e
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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 javax.jcr.Repository;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+
+public abstract class AbstractJcrTestCase extends TestCase {
+       private final static Log log = LogFactory.getLog(AbstractJcrTestCase.class);
+
+       private Repository repository;
+       private Session session = null;
+
+       protected abstract File getRepositoryFile() throws Exception;
+
+       protected abstract Repository createRepository() throws Exception;
+
+       @Override
+       protected void setUp() throws Exception {
+               File homeDir = getHomeDir();
+               FileUtils.deleteDirectory(homeDir);
+               repository = createRepository();
+       }
+
+       protected File getHomeDir() {
+               File homeDir = new File(System.getProperty("java.io.tmpdir"),
+                               AbstractJcrTestCase.class.getSimpleName() + "-"
+                                               + System.getProperty("user.name"));
+               return homeDir;
+       }
+
+       @Override
+       protected void tearDown() throws Exception {
+               if (session != null) {
+                       session.logout();
+                       if (log.isDebugEnabled())
+                               log.debug("Logout session");
+               }
+       }
+
+       protected Session session() {
+               if (session == null) {
+                       try {
+                               if (log.isDebugEnabled())
+                                       log.debug("Login session");
+                               session = getRepository().login(
+                                               new SimpleCredentials("demo", "demo".toCharArray()));
+                       } catch (Exception e) {
+                               throw new ArgeoException("Cannot login to repository", e);
+                       }
+               }
+               return session;
+       }
+
+       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
+        */
+       protected void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/main/resources/org/argeo/jcr/argeo.cnd b/trunk/server/runtime/org.argeo.server.jcr/src/main/resources/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/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/AbstractInternalJackrabbitTestCase.java b/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/AbstractInternalJackrabbitTestCase.java
new file mode 100644 (file)
index 0000000..23281d0
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.File;
+
+import javax.jcr.Repository;
+
+import org.apache.jackrabbit.core.TransientRepository;
+import org.argeo.jcr.unit.AbstractJcrTestCase;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+
+/** Factorizes configuration of an in memory transient repository */
+public abstract class AbstractInternalJackrabbitTestCase extends
+               AbstractJcrTestCase {
+       protected File getRepositoryFile() throws Exception {
+               Resource res = new ClassPathResource(
+                               "org/argeo/server/jcr/repository-memory.xml");
+               return res.getFile();
+       }
+
+       protected Repository createRepository() throws Exception {
+               Repository repository = new TransientRepository(getRepositoryFile(),
+                               getHomeDir());
+               return repository;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/CollectionsObject.java b/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/CollectionsObject.java
new file mode 100644 (file)
index 0000000..1cbb931
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CollectionsObject {
+       private String id;
+       private String label;
+       private SimpleObject simpleObject;
+       private List<String> stringList = new ArrayList<String>();
+       private Map<String, Float> floatMap = new HashMap<String, Float>();
+       private Map<SimpleObject, String> objectMap = new HashMap<SimpleObject, String>();
+       private Map<String, Map<String, String>> mapOfMaps = new HashMap<String, Map<String, String>>();
+
+       public String getId() {
+               return id;
+       }
+
+       public void setId(String id) {
+               this.id = id;
+       }
+
+       public String getLabel() {
+               return label;
+       }
+
+       public void setLabel(String label) {
+               this.label = label;
+       }
+
+       public SimpleObject getSimpleObject() {
+               return simpleObject;
+       }
+
+       public void setSimpleObject(SimpleObject simpleObject) {
+               this.simpleObject = simpleObject;
+       }
+
+       public List<String> getStringList() {
+               return stringList;
+       }
+
+       public void setStringList(List<String> stringList) {
+               this.stringList = stringList;
+       }
+
+       public Map<String, Float> getFloatMap() {
+               return floatMap;
+       }
+
+       public void setFloatMap(Map<String, Float> floatMap) {
+               this.floatMap = floatMap;
+       }
+
+       public Map<SimpleObject, String> getObjectMap() {
+               return objectMap;
+       }
+
+       public void setObjectMap(Map<SimpleObject, String> objectMap) {
+               this.objectMap = objectMap;
+       }
+
+       public Map<String, Map<String, String>> getMapOfMaps() {
+               return mapOfMaps;
+       }
+
+       public void setMapOfMaps(Map<String, Map<String, String>> mapOfMaps) {
+               this.mapOfMaps = mapOfMaps;
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/MapperTest.java b/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/MapperTest.java
new file mode 100644 (file)
index 0000000..5486ff4
--- /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.jcr;
+
+import javax.jcr.Node;
+
+import org.argeo.jcr.spring.BeanNodeMapper;
+
+public class MapperTest extends AbstractInternalJackrabbitTestCase {
+       public void testSimpleObject() throws Exception {
+               SimpleObject mySo = new SimpleObject();
+               mySo.setInteger(100);
+               mySo.setString("hello world");
+
+               OtherObject oo1 = new OtherObject();
+               oo1.setKey("someKey");
+               oo1.setValue("stringValue");
+               mySo.setOtherObject(oo1);
+
+               OtherObject oo2 = new OtherObject();
+               oo2.setKey("anotherSimpleObject");
+               oo2.setValue(new SimpleObject());
+               mySo.setAnotherObject(oo2);
+
+               BeanNodeMapper bnm = new BeanNodeMapper();
+
+               Node node = bnm.save(session(), mySo);
+               session().save();
+               JcrUtils.debug(node);
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/OtherObject.java b/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/OtherObject.java
new file mode 100644 (file)
index 0000000..5cce8ff
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+public class OtherObject {
+       private String key;
+       private Object value;
+
+       public String getKey() {
+               return key;
+       }
+
+       public void setKey(String key) {
+               this.key = key;
+       }
+
+       public Object getValue() {
+               return value;
+       }
+
+       public void setValue(Object value) {
+               this.value = value;
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/SimpleObject.java b/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/SimpleObject.java
new file mode 100644 (file)
index 0000000..cdc32fc
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.UUID;
+
+public class SimpleObject {
+       private String string;
+       private String uuid = UUID.randomUUID().toString();
+       private Integer integer;
+       private OtherObject otherObject;
+       private OtherObject anotherObject;
+
+       public String getString() {
+               return string;
+       }
+
+       public void setString(String sting) {
+               this.string = sting;
+       }
+
+       public Integer getInteger() {
+               return integer;
+       }
+
+       public void setInteger(Integer integer) {
+               this.integer = integer;
+       }
+
+       public OtherObject getOtherObject() {
+               return otherObject;
+       }
+
+       public void setOtherObject(OtherObject otherObject) {
+               this.otherObject = otherObject;
+       }
+
+       public OtherObject getAnotherObject() {
+               return anotherObject;
+       }
+
+       public void setAnotherObject(OtherObject anotherObject) {
+               this.anotherObject = anotherObject;
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               return string.equals(((SimpleObject) obj).string);
+       }
+
+       @Override
+       public int hashCode() {
+               return string.hashCode();
+       }
+
+       public void setUuid(String uuid) {
+               this.uuid = uuid;
+       }
+
+       public String getUuid() {
+               return uuid;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/tabular/JcrTabularTest.java b/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/jcr/tabular/JcrTabularTest.java
new file mode 100644 (file)
index 0000000..2045f9a
--- /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.jcr.AbstractInternalJackrabbitTestCase;
+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 AbstractInternalJackrabbitTestCase {
+       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/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/server/jcr/JcrResourceAdapterTest.java b/trunk/server/runtime/org.argeo.server.jcr/src/test/java/org/argeo/server/jcr/JcrResourceAdapterTest.java
new file mode 100644 (file)
index 0000000..b691572
--- /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.jcr.AbstractInternalJackrabbitTestCase;
+import org.argeo.jcr.JcrResourceAdapter;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+
+public class JcrResourceAdapterTest extends AbstractInternalJackrabbitTestCase {
+       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/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/log4j.properties b/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/log4j.properties
new file mode 100644 (file)
index 0000000..ca995af
--- /dev/null
@@ -0,0 +1,13 @@
+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
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy00.xls b/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy00.xls
new file mode 100644 (file)
index 0000000..e5846fe
Binary files /dev/null and b/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy00.xls differ
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy01.xls b/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy01.xls
new file mode 100644 (file)
index 0000000..b5c6b55
Binary files /dev/null and b/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy01.xls differ
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy02.xls b/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy02.xls
new file mode 100644 (file)
index 0000000..d73bc66
Binary files /dev/null and b/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy02.xls differ
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy03.xls b/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy03.xls
new file mode 100644 (file)
index 0000000..0759cb9
Binary files /dev/null and b/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/dummy03.xls differ
diff --git a/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/repository-h2.xml b/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/repository-h2.xml
new file mode 100644 (file)
index 0000000..ef3f0c4
--- /dev/null
@@ -0,0 +1,98 @@
+<?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>
+       <!-- 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/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/repository-memory.xml b/trunk/server/runtime/org.argeo.server.jcr/src/test/resources/org/argeo/server/jcr/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/trunk/server/runtime/org.argeo.server.json/.classpath b/trunk/server/runtime/org.argeo.server.json/.classpath
new file mode 100644 (file)
index 0000000..607f058
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+       <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/trunk/server/runtime/org.argeo.server.json/.project b/trunk/server/runtime/org.argeo.server.json/.project
new file mode 100644 (file)
index 0000000..e32fd4a
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.json</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/trunk/server/runtime/org.argeo.server.json/.settings/org.eclipse.jdt.core.prefs b/trunk/server/runtime/org.argeo.server.json/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..d5df3e8
--- /dev/null
@@ -0,0 +1,67 @@
+#Sun Feb 21 11:16:01 CET 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/server/runtime/org.argeo.server.json/.settings/org.maven.ide.eclipse.prefs b/trunk/server/runtime/org.argeo.server.json/.settings/org.maven.ide.eclipse.prefs
new file mode 100644 (file)
index 0000000..cb4dc85
--- /dev/null
@@ -0,0 +1,9 @@
+#Wed Sep 16 09:47:45 CEST 2009
+activeProfiles=
+eclipse.preferences.version=1
+fullBuildGoals=process-test-resources
+includeModules=false
+resolveWorkspaceProjects=true
+resourceFilterGoals=process-resources resources\:testResources
+skipCompilerPlugin=true
+version=1
diff --git a/trunk/server/runtime/org.argeo.server.json/build.properties b/trunk/server/runtime/org.argeo.server.json/build.properties
new file mode 100644 (file)
index 0000000..65cb73b
--- /dev/null
@@ -0,0 +1,2 @@
+source.. = src/main/java/,\
+           src/test/java/
diff --git a/trunk/server/runtime/org.argeo.server.json/pom.xml b/trunk/server/runtime/org.argeo.server.json/pom.xml
new file mode 100644 (file)
index 0000000..362acf9
--- /dev/null
@@ -0,0 +1,87 @@
+<?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.server</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.json</artifactId>
+       <name>Commons Server JSON</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>
+                                                       org.argeo.*
+                                               </Export-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- Argeo Commons -->
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.core</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- JSON  -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.codehaus.jackson.mapper</artifactId>
+               </dependency>
+
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.web.servlet</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.osgi.core</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.eclipse.osgi</artifactId>
+               </dependency>
+
+               <!-- J2EE -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.servlet</artifactId>
+               </dependency>
+
+               <!-- Logging -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+
+               <!-- TEST -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>junit</artifactId>
+                       <scope>test</scope>
+               </dependency>
+
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/GenericJsonDeserializer.java b/trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/GenericJsonDeserializer.java
new file mode 100644 (file)
index 0000000..6f039c4
--- /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.server.json;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.codehaus.jackson.JsonFactory;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.ObjectCodec;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.codehaus.jackson.map.ObjectMapper;
+
+public class GenericJsonDeserializer<T> extends JsonDeserializer<T> {
+       private final static Log log = LogFactory
+                       .getLog(GenericJsonDeserializer.class);
+
+       private JsonFactory jsonFactory = new JsonFactory();
+       private ObjectCodec objectCodec = new ObjectMapper();
+       private JsonObjectFactory defaultObjectFactory = new JsonObjectFactoryImpl();
+
+       private String typeField = "type";
+
+       private List<JsonObjectFactory> objectFactories = new ArrayList<JsonObjectFactory>();
+
+       @SuppressWarnings("unchecked")
+       @Override
+       public T deserialize(JsonParser parser, DeserializationContext ctxt)
+                       throws IOException, JsonProcessingException {
+               // first read as Json DOM in order to extract the type
+               // TODO: optimize with streaming API
+               JsonNode root = parser.readValueAsTree();
+               String type = root.get(typeField).getTextValue();
+
+               // Write it back as a string
+               StringWriter writer = new StringWriter();
+               JsonGenerator generator = jsonFactory.createJsonGenerator(writer);
+               generator.setCodec(objectCodec);
+               generator.writeTree(root);
+               String str = writer.toString();
+
+               if (log.isTraceEnabled())
+                       log.debug("Deserialize object of type=" + type + ", str=" + str);
+
+               JsonObjectFactory objectFactory = null;
+               jofs: for (JsonObjectFactory jof : objectFactories) {
+                       if (jof.supports(type)) {
+                               objectFactory = jof;
+                               break jofs;
+                       }
+               }
+
+               if (objectFactory == null)
+                       objectFactory = defaultObjectFactory;
+
+               if (objectFactory == null || !objectFactory.supports(type))
+                       throw new ArgeoException(
+                                       "Cannot find JSON object factory for type " + type);
+
+               return (T) objectFactory.readValue(type, str);
+       }
+
+       public void setTypeField(String typeField) {
+               this.typeField = typeField;
+       }
+
+       public void setObjectFactories(List<JsonObjectFactory> objectFactories) {
+               this.objectFactories = objectFactories;
+       }
+
+       public List<JsonObjectFactory> getObjectFactories() {
+               return objectFactories;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/JsonObjectFactory.java b/trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/JsonObjectFactory.java
new file mode 100644 (file)
index 0000000..0fa67c6
--- /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.server.json;
+
+
+public interface JsonObjectFactory {
+       public Boolean supports(String type);
+
+       public <T> T readValue(String type, String str);
+}
diff --git a/trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/JsonObjectFactoryImpl.java b/trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/JsonObjectFactoryImpl.java
new file mode 100644 (file)
index 0000000..4ed46c5
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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.json;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.osgi.framework.BundleContext;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.osgi.context.BundleContextAware;
+import org.springframework.osgi.util.BundleDelegatingClassLoader;
+
+public class JsonObjectFactoryImpl implements JsonObjectFactory,
+               BundleContextAware, InitializingBean {
+       private final static Log log = LogFactory
+                       .getLog(JsonObjectFactoryImpl.class);
+
+       private BundleContext bundleContext;
+       private ClassLoader classLoader = getClass().getClassLoader();
+
+       private ObjectMapper objectMapper = new ObjectMapper();
+       private Map<String, Class<?>> supportedTypes = new HashMap<String, Class<?>>();
+
+       public Boolean supports(String type) {
+               if (supportedTypes.containsKey(type))
+                       return true;
+
+               return loadClass(type) != null ? true : false;
+       }
+
+       @SuppressWarnings("unchecked")
+       public <T> T readValue(String type, String str) {
+               final Class<?> clss;
+               if (supportedTypes.containsKey(type))
+                       clss = supportedTypes.get(type);
+               else {
+                       clss = loadClass(type);
+                       if (clss == null)
+                               throw new ArgeoException("Cannot find type " + type);
+               }
+
+               try {
+                       return (T) objectMapper.readValue(str, clss);
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot deserialize " + str
+                                       + " (type=" + type + ")", e);
+               }
+       }
+
+       public void setSupportedTypes(Map<String, Class<?>> supportedTypes) {
+               this.supportedTypes = supportedTypes;
+       }
+
+       protected Class<?> loadClass(String type) {
+               try {
+                       return classLoader.loadClass(type);
+               } catch (ClassNotFoundException e) {
+                       if (log.isDebugEnabled())
+                               log.debug("BundleDelegatingClassLoader.loadClass failed: " + e);
+               }
+
+               return null;
+       }
+
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+       public void afterPropertiesSet() throws Exception {
+               classLoader = BundleDelegatingClassLoader
+                               .createBundleClassLoaderFor(bundleContext.getBundle());
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/JsonServerMapper.java b/trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/JsonServerMapper.java
new file mode 100644 (file)
index 0000000..a7c860c
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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.json;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.server.Deserializer;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.DeserializationProblemHandler;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.codehaus.jackson.map.deser.CustomDeserializerFactory;
+import org.codehaus.jackson.map.deser.StdDeserializerProvider;
+import org.springframework.beans.factory.InitializingBean;
+
+public class JsonServerMapper extends JsonServerSerializer implements
+               Deserializer, InitializingBean {
+       private final static Log log = LogFactory.getLog(JsonServerMapper.class);
+
+       private Class<?> targetClass;
+
+       private Map<Class<?>, JsonDeserializer<?>> deserializers = new HashMap<Class<?>, JsonDeserializer<?>>();
+
+       @SuppressWarnings("unchecked")
+       public void afterPropertiesSet() throws Exception {
+               CustomDeserializerFactory dsf = new CustomDeserializerFactory();
+               for (Class clss : deserializers.keySet()) {
+                       dsf.addSpecificMapping(clss, deserializers.get(clss));
+                       if (log.isDebugEnabled())
+                               log.debug("Add JSON mapping for " + clss);
+               }
+               StdDeserializerProvider sdp = new StdDeserializerProvider(dsf);
+               getObjectMapper().setDeserializerProvider(sdp);
+               // ignore unkown properties
+               getObjectMapper().getDeserializationConfig().addHandler(
+                               new DeserializationProblemHandler() {
+                                       public boolean handleUnknownProperty(
+                                                       DeserializationContext ctxt,
+                                                       JsonDeserializer<?> deserializer, Object bean,
+                                                       String propertyName) throws IOException,
+                                                       JsonProcessingException {
+                                               if (log.isTraceEnabled())
+                                                       log.debug("Ignore property " + propertyName
+                                                                       + " in bean " + bean);
+                                               ctxt.getParser().skipChildren();
+                                               return true;
+                                       }
+                               });
+       }
+
+       public Object deserialize(Reader reader) {
+               try {
+                       if (log.isTraceEnabled()) {
+                               String str = IOUtils.toString(reader);
+                               log.debug(str);
+                               reader = new StringReader(str);
+                       }
+
+                       return getObjectMapper().readValue(reader, targetClass);
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot deserialize " + reader, e);
+               }
+
+       }
+
+       public <T> T deserialize(Reader reader, Class<T> clss) {
+               try {
+                       if (log.isTraceEnabled()) {
+                               String str = IOUtils.toString(reader);
+                               log.debug(str);
+                               reader = new StringReader(str);
+                       }
+
+                       return getObjectMapper().readValue(reader, clss);
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot deserialize " + reader, e);
+               }
+
+       }
+
+       public Object deserialize(String content) {
+               StringReader reader = new StringReader(content);
+               try {
+                       return deserialize(reader);
+               } finally {
+                       IOUtils.closeQuietly(reader);
+               }
+       }
+
+       public void setTargetClass(Class<?> targetClass) {
+               this.targetClass = targetClass;
+       }
+
+       public void setDeserializers(
+                       Map<Class<?>, JsonDeserializer<?>> deserializers) {
+               this.deserializers = deserializers;
+       }
+
+       public Class<?> getTargetClass() {
+               return targetClass;
+       }
+
+       public Map<Class<?>, JsonDeserializer<?>> getDeserializers() {
+               return deserializers;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/JsonServerSerializer.java b/trunk/server/runtime/org.argeo.server.json/src/main/java/org/argeo/server/json/JsonServerSerializer.java
new file mode 100644 (file)
index 0000000..696cdf8
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * 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.json;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.server.Serializer;
+import org.argeo.server.ServerSerializer;
+import org.codehaus.jackson.JsonFactory;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+import org.codehaus.jackson.map.SerializerFactory;
+import org.codehaus.jackson.map.SerializerProvider;
+import org.codehaus.jackson.map.ser.StdSerializerProvider;
+
+public class JsonServerSerializer implements ServerSerializer, Serializer {
+       private final static Log log = LogFactory
+                       .getLog(JsonServerSerializer.class);
+
+       private JsonFactory jsonFactory = new JsonFactory();
+       private ObjectMapper objectMapper = new ObjectMapper();
+       private SerializerProvider serializerProvider = new CustomSerializerProvider();
+
+       private Boolean prettyPrint = false;
+
+       private Boolean asHtml = false;
+
+       private String contentTypeCharset = "UTF-8";
+
+       // private Map<Class<?>,String> ignoredFields = new HashMap<Class<?>,
+       // String>();
+
+       public JsonServerSerializer() {
+               objectMapper.setSerializerProvider(serializerProvider);
+       }
+
+       @SuppressWarnings("restriction")
+       public void serialize(Object obj, HttpServletRequest request,
+                       HttpServletResponse response) {
+               if (asHtml)
+                       response.setContentType("text/html;charset=" + contentTypeCharset);
+               else
+                       response.setContentType("application/json;charset="
+                                       + contentTypeCharset);
+
+               try {
+                       if (asHtml)
+                               response.getWriter().append("<pre>");
+
+                       serialize(obj, response.getWriter());
+
+                       if (asHtml)
+                               response.getWriter().append("</pre>");
+
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot open response stream.", e);
+               }
+       }
+
+       public void serialize(Object obj, Writer writer) {
+               serializeAndLog(obj);
+
+               JsonGenerator jsonGenerator = null;
+               try {
+                       jsonGenerator = jsonFactory.createJsonGenerator(writer);
+
+                       if (prettyPrint)
+                               jsonGenerator.useDefaultPrettyPrinter();
+
+                       objectMapper.writeValue(jsonGenerator, obj);
+                       jsonGenerator.flush();
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot serialize " + obj, e);
+               } finally {
+                       if (jsonGenerator != null)
+                               try {
+                                       jsonGenerator.close();
+                               } catch (IOException e) {
+                                       if (log.isTraceEnabled())
+                                               log.error("Cannot close JSON generator", e);
+                               }
+               }
+       }
+
+       @Deprecated
+       public void serialize(Writer writer, Object obj) {
+               serialize(obj, writer);
+       }
+
+       protected void serializeAndLog(Object obj) {
+               if (!log.isTraceEnabled())
+                       return;
+
+               JsonGenerator jsonGenerator = null;
+               try {
+                       StringWriter stringWriter = new StringWriter();
+                       jsonGenerator = jsonFactory.createJsonGenerator(stringWriter);
+                       jsonGenerator.useDefaultPrettyPrinter();
+                       objectMapper.writeValue(jsonGenerator, obj);
+                       jsonGenerator.close();
+                       log.debug(stringWriter.toString());
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot log JSON", e);
+               } finally {
+                       if (jsonGenerator != null)
+                               try {
+                                       jsonGenerator.close();
+                               } catch (IOException e) {
+                                       if (log.isTraceEnabled())
+                                               log.error("Cannot close JSON generator", e);
+                               }
+
+               }
+       }
+
+       public void setPrettyPrint(Boolean prettyPrint) {
+               this.prettyPrint = prettyPrint;
+       }
+
+       protected ObjectMapper getObjectMapper() {
+               return objectMapper;
+       }
+
+       public void setContentTypeCharset(String contentTypeCharset) {
+               this.contentTypeCharset = contentTypeCharset;
+       }
+
+       static class CustomSerializerProvider extends StdSerializerProvider {
+
+               public CustomSerializerProvider() {
+                       super();
+               }
+
+               public CustomSerializerProvider(SerializationConfig config,
+                               StdSerializerProvider src, SerializerFactory f) {
+                       super(config, src, f);
+               }
+
+               protected StdSerializerProvider createInstance(
+                               SerializationConfig config, SerializerFactory jsf) {
+                       return new CustomSerializerProvider(config, this, jsf);
+               }
+
+               @Override
+               public JsonSerializer<Object> getUnknownTypeSerializer(
+                               Class<?> unknownType) {
+                       JsonSerializer<Object> res = new JsonSerializer<Object>() {
+                               public void serialize(Object value, JsonGenerator jgen,
+                                               SerializerProvider provider)
+                                               throws JsonMappingException {
+                                       if (log.isDebugEnabled())
+                                               log.warn("Unknown serializer for "
+                                                               + value.getClass().getName());
+                                       try {
+                                               jgen.writeNull();
+                                       } catch (Exception e) {
+                                               throw new ArgeoException("Cannot write null", e);
+                                       }
+                               }
+
+                       };
+
+                       return res;
+               }
+
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jxl/.classpath b/trunk/server/runtime/org.argeo.server.jxl/.classpath
new file mode 100644 (file)
index 0000000..ab16ae2
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+       <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+       <classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/trunk/server/runtime/org.argeo.server.jxl/.project b/trunk/server/runtime/org.argeo.server.jxl/.project
new file mode 100644 (file)
index 0000000..10df72a
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.jxl</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/trunk/server/runtime/org.argeo.server.jxl/.settings/org.eclipse.jdt.core.prefs b/trunk/server/runtime/org.argeo.server.jxl/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..d991f4b
--- /dev/null
@@ -0,0 +1,67 @@
+#Sun Feb 21 11:16:56 CET 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/server/runtime/org.argeo.server.jxl/.settings/org.maven.ide.eclipse.prefs b/trunk/server/runtime/org.argeo.server.jxl/.settings/org.maven.ide.eclipse.prefs
new file mode 100644 (file)
index 0000000..ef3da62
--- /dev/null
@@ -0,0 +1,9 @@
+#Thu Oct 01 12:05:59 CEST 2009
+activeProfiles=
+eclipse.preferences.version=1
+fullBuildGoals=process-test-resources
+includeModules=false
+resolveWorkspaceProjects=true
+resourceFilterGoals=process-resources resources\:testResources
+skipCompilerPlugin=true
+version=1
diff --git a/trunk/server/runtime/org.argeo.server.jxl/build.properties b/trunk/server/runtime/org.argeo.server.jxl/build.properties
new file mode 100644 (file)
index 0000000..d5d2601
--- /dev/null
@@ -0,0 +1,4 @@
+additional.bundles = com.springsource.junit
+source.. = src/main/java/,\
+           src/test/java/,\
+           src/test/resources/
diff --git a/trunk/server/runtime/org.argeo.server.jxl/pom.xml b/trunk/server/runtime/org.argeo.server.jxl/pom.xml
new file mode 100644 (file)
index 0000000..aacfdb1
--- /dev/null
@@ -0,0 +1,69 @@
+<?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.server</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.jxl</artifactId>
+       <name>Commons Server JXL</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>
+                                                       org.argeo.*
+                                               </Export-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- Argeo Commons -->
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.core</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <!-- JXL  -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>jxl</artifactId>
+               </dependency>
+
+               <!-- TEST -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>junit</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.log4j</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+                       <scope>test</scope>
+               </dependency>
+
+
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.jxl/src/main/java/org/argeo/server/jxl/dao/JxlDaoSupport.java b/trunk/server/runtime/org.argeo.server.jxl/src/main/java/org/argeo/server/jxl/dao/JxlDaoSupport.java
new file mode 100644 (file)
index 0000000..6d59961
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * 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.jxl.dao;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import jxl.Cell;
+import jxl.CellType;
+import jxl.FormulaCell;
+import jxl.JXLException;
+import jxl.LabelCell;
+import jxl.NumberCell;
+import jxl.Sheet;
+import jxl.Workbook;
+import jxl.WorkbookSettings;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.server.dao.AbstractTabularDaoSupport;
+import org.argeo.server.dao.LightDaoSupport;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.util.StringUtils;
+
+public class JxlDaoSupport extends AbstractTabularDaoSupport implements
+               LightDaoSupport, ApplicationContextAware, InitializingBean {
+       private final static Log log = LogFactory.getLog(JxlDaoSupport.class);
+
+       private String encoding = "cp1252";
+       private Locale locale = null;
+
+       protected void load(InputStream in, List<Reference> references) {
+               try {
+                       WorkbookSettings workbookSettings = new WorkbookSettings();
+                       workbookSettings.setEncoding(encoding);
+                       if (locale != null)
+                               workbookSettings.setLocale(locale);
+                       Workbook workbook = Workbook.getWorkbook(in, workbookSettings);
+                       for (Sheet sheet : workbook.getSheets()) {
+                               loadSheet(sheet, references);
+                       }
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot load workbook", e);
+               }
+       }
+
+       protected void loadSheet(Sheet sheet, List<Reference> references)
+                       throws JXLException {
+               String sheetName = sheet.getName();
+               if (log.isTraceEnabled())
+                       log.debug("Instantiate sheet " + sheetName);
+
+               String tableName;
+               int hashIndex = sheetName.lastIndexOf('#');
+               if (hashIndex >= 0) {
+                       tableName = sheetName.substring(0, hashIndex);
+               } else {
+                       tableName = sheetName;
+               }
+
+               Class<?> clss = findClassToInstantiate(tableName);
+
+               if (hashIndex >= 0) {
+                       // see
+                       // http://stackoverflow.com/questions/451452/valid-characters-for-excel-sheet-names
+                       BeanWrapper bw = newBeanWrapper(clss);
+                       StringTokenizer espSt = new StringTokenizer(sheetName
+                                       .substring(hashIndex + 1), "&=");
+                       String keyProperty = null;
+                       while (espSt.hasMoreTokens()) {
+                               String fieldName = espSt.nextToken();
+                               if (keyProperty == null)
+                                       keyProperty = fieldName;
+                               if (!espSt.hasMoreTokens())
+                                       throw new ArgeoException("Badly formatted sheetname "
+                                                       + sheetName);
+                               String fieldValue = espSt.nextToken();
+                               bw.setPropertyValue(fieldName, fieldValue);
+                               loadAsObject(bw, sheet, references);
+                               saveOrUpdate(bw.getPropertyValue(keyProperty), bw
+                                               .getWrappedInstance(), clss);
+                       }
+
+               } else {
+
+                       Cell[] firstRow = sheet.getRow(0);
+                       String keyProperty = firstRow[0].getContents();
+
+                       if (keyProperty.charAt(keyProperty.length() - 1) == '>') {
+                               loadAsColumns(clss, keyProperty.substring(0, keyProperty
+                                               .length() - 1), sheet, firstRow, references);
+                       } else {
+                               loadAsRows(clss, keyProperty, sheet, firstRow, references);
+                       }
+               }
+       }
+
+       protected void loadAsRows(Class<?> clss, String keyProperty, Sheet sheet,
+                       Cell[] firstRow, List<Reference> references) throws JXLException {
+               for (int row = 1; row < sheet.getRows(); row++) {
+                       if (log.isTraceEnabled())
+                               log.trace(" row " + row);
+
+                       Cell[] currentRow = sheet.getRow(row);
+                       BeanWrapper bw = newBeanWrapper(clss);
+                       cells: for (int col = 0; col < firstRow.length; col++) {
+                               String pName = firstRow[col].getContents();
+
+                               if (col < currentRow.length) {
+                                       Cell cell = currentRow[col];
+                                       if (overrideCell(cell, bw, pName, keyProperty, row,
+                                                       references))
+                                               continue cells;
+                                       loadCell(cell, bw, pName, keyProperty, row, references);
+                               }
+                       }// cells
+
+                       saveOrUpdate(bw.getPropertyValue(keyProperty), bw
+                                       .getWrappedInstance(), clss);
+                       // tempRefs.get(sheet.getName()).add(bw.getWrappedInstance());
+                       registerInTabularView(sheet.getName(), bw.getWrappedInstance());
+               }
+       }
+
+       protected void loadAsColumns(Class<?> clss, String keyProperty,
+                       Sheet sheet, Cell[] firstRow, List<Reference> references)
+                       throws JXLException {
+               Cell[] firstColumn = sheet.getColumn(0);
+
+               for (int col = 1; col < firstRow.length; col++) {
+                       if (log.isTraceEnabled())
+                               log.trace(" column " + col);
+                       BeanWrapper bw = newBeanWrapper(clss);
+                       Cell[] column = sheet.getColumn(col);
+                       for (int row = 0; row < column.length; row++) {
+                               Cell cell = column[row];
+
+                               String propertyName;
+                               if (row == 0)
+                                       propertyName = keyProperty;
+                               else
+                                       propertyName = firstColumn[row].getContents();
+
+                               Class<?> rowType = bw.getPropertyType(propertyName);
+                               if (log.isTraceEnabled())
+                                       log.trace(" " + propertyName + " rowType="
+                                                       + rowType.getName());
+                               if (Map.class.isAssignableFrom(rowType)) {
+                                       if (log.isTraceEnabled())
+                                               log.trace("  start building map " + propertyName);
+                                       row++;
+                                       Map<Object, Object> map = new HashMap<Object, Object>();
+                                       String firstColContents = firstColumn[row].getContents();
+                                       mapRows: for (; row < column.length; row++) {
+                                               cell = column[row];
+                                               Object key = firstColContents;
+                                               if (log.isTraceEnabled())
+                                                       log.trace("   row=" + row + ", firstColContents="
+                                                                       + firstColContents + ", key=" + key
+                                                                       + ", type=" + cell.getType());
+                                               Object cellValue = getCellValue(cell);
+                                               map.put(key, cellValue);
+
+                                               // check next row too see if one should break
+                                               if (row < firstColumn.length - 1)
+                                                       firstColContents = firstColumn[row + 1]
+                                                                       .getContents();
+                                               if (bw.isWritableProperty(firstColContents)
+                                                               || firstColContents.trim().equals("")
+                                                               || row == firstColumn.length - 1) {
+                                                       bw.setPropertyValue(propertyName, map);
+                                                       if (log.isTraceEnabled())
+                                                               log.trace(" set map " + propertyName
+                                                                               + " of size " + map.size());
+                                                       break mapRows;// map is over
+                                               }
+                                       }
+                               } else {
+                                       loadCell(cell, bw, propertyName, keyProperty, row,
+                                                       references);
+                               }
+
+                       }
+                       saveOrUpdate(bw.getPropertyValue(keyProperty), bw
+                                       .getWrappedInstance(), clss);
+                       // tempRefs.get(sheet.getName()).add(bw.getWrappedInstance());
+                       registerInTabularView(sheet.getName(), bw.getWrappedInstance());
+               }// columns
+       }
+
+       protected void loadAsObject(BeanWrapper bw, Sheet sheet,
+                       List<Reference> references) {
+               Cell[] firstColumn = sheet.getColumn(0);
+               for (int row = 0; row < firstColumn.length; row++) {
+                       if (log.isTraceEnabled())
+                               log.trace(" row " + row);
+                       Cell[] currentRow = sheet.getRow(row);
+                       String propertyName = firstColumn[row].getContents();
+                       Class<?> rowType = bw.getPropertyType(propertyName);
+                       if (Map.class.isAssignableFrom(rowType)) {
+                               Map<Object, Object> map = new HashMap<Object, Object>();
+                               if (currentRow.length == 1
+                                               || currentRow[1].getContents().trim().equals("")) {
+                                       // simple map
+                               } else {
+                                       // map of maps
+                                       List<Object> subKeys = new ArrayList<Object>();
+                                       for (int col = 1; col < currentRow.length; col++) {
+                                               subKeys.add(getCellValue(currentRow[col]));
+                                       }
+                                       if (log.isTraceEnabled())
+                                               log.trace("   subKeys=" + subKeys);
+                                       row++;
+                                       String firstColContents = firstColumn[row].getContents();
+                                       mapRows: for (; row < firstColumn.length; row++) {
+                                               currentRow = sheet.getRow(row);
+
+                                               Object key = firstColContents;
+                                               Map<Object, Object> subMap = new HashMap<Object, Object>();
+
+                                               for (int col = 1; col < currentRow.length
+                                                               && col < subKeys.size() + 1; col++) {
+                                                       Object subKey = subKeys.get(col - 1);
+                                                       Cell cell = currentRow[col];
+                                                       if (log.isTraceEnabled())
+                                                               log.trace("   row=" + row
+                                                                               + ", firstColContents="
+                                                                               + firstColContents + ", subKey="
+                                                                               + subKey + ", type=" + cell.getType());
+                                                       Object cellValue = getCellValue(cell);
+                                                       subMap.put(subKey, cellValue);
+                                               }
+                                               map.put(key, subMap);
+
+                                               // check next row too see if one should break
+                                               if (row < firstColumn.length - 1)
+                                                       firstColContents = firstColumn[row + 1]
+                                                                       .getContents();
+                                               if (bw.isWritableProperty(firstColContents)
+                                                               || firstColContents.trim().equals("")
+                                                               || row == firstColumn.length - 1) {
+                                                       log.trace(map);
+                                                       bw.setPropertyValue(propertyName, map);
+                                                       if (log.isTraceEnabled())
+                                                               log.trace(" set map " + propertyName
+                                                                               + " of size " + map.size());
+                                                       break mapRows;// map is over
+                                               }
+                                       }
+
+                               }
+                       } else if (List.class.isAssignableFrom(rowType)) {
+                               throw new UnsupportedOperationException();
+                       } else {
+                               bw.setPropertyValue(propertyName, getCellValue(currentRow[1]));
+                       }
+               }
+       }
+
+       protected void loadCell(Cell cell, BeanWrapper bw, String propertyName,
+                       String keyProperty, Integer row, List<Reference> references)
+                       throws JXLException {
+
+               if (cell instanceof FormulaCell) {
+                       String formula = ((FormulaCell) cell).getFormula();
+                       int index = formula.indexOf('!');
+                       if (index < 0)
+                               throw new ArgeoException("Cannot interpret formula "
+                                               + formula);
+                       ;
+                       String targetSheet = formula.substring(0, index);
+                       // assume no double letters!!
+                       String targetRowStr = formula.substring(index + 2);
+                       if (targetRowStr.charAt(0) == '$')
+                               targetRowStr = targetRowStr.substring(1);
+                       Integer targetRow = Integer.parseInt(targetRowStr);
+                       references.add(new TabularInternalReference(
+                                       bw.getWrappedInstance(), propertyName, targetSheet,
+                                       targetRow));
+
+                       if (log.isTraceEnabled())
+                               log.debug("  formula: " + formula + " | content: "
+                                               + cell.getContents() + " | targetSheet=" + targetSheet
+                                               + ", targetRow=" + targetRow);
+               } else {
+                       Object cellValue = getCellValue(cell);
+
+                       if (propertyName.equals(keyProperty)
+                                       && !StringUtils.hasText(cellValue.toString())) {
+                               // auto allocate key column if empty
+                               cellValue = Integer.toString(row);
+                       }
+
+                       if (propertyName.charAt(0) == '#') {// externalRef
+                               references.add(new Reference(bw.getWrappedInstance(),
+                                               propertyName.substring(1), cellValue.toString()));
+                       } else {
+                               bw.setPropertyValue(propertyName, cellValue);
+                       }
+
+                       if (log.isTraceEnabled())
+                               log.debug("  " + propertyName + "=" + cellValue);
+               }
+
+       }
+
+       protected Object getCellValue(Cell cell) {
+               Object contents;
+               if (cell.getType() == CellType.LABEL) {
+                       LabelCell lc = (LabelCell) cell;
+                       contents = lc.getString();
+               } else if (cell.getType() == CellType.NUMBER) {
+                       NumberCell nc = (NumberCell) cell;
+                       contents = nc.getValue();
+               } else {
+                       contents = cell.getContents();
+               }
+               return contents;
+       }
+
+       /** Returns true if property was set (thus bypassing standard process). */
+       protected Boolean overrideCell(Cell cell, BeanWrapper bw,
+                       String propertyName, String keyProperty, Integer row,
+                       List<Reference> references) {
+               return false;
+       }
+
+       public void setEncoding(String encoding) {
+               this.encoding = encoding;
+       }
+
+       public void setLocale(Locale locale) {
+               this.locale = locale;
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/CollectionsObject.java b/trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/CollectionsObject.java
new file mode 100644 (file)
index 0000000..2c6308f
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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.jxl.dao;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CollectionsObject {
+       private String id;
+       private String label;
+       private SimpleObject simpleObject;
+       private List<String> stringList = new ArrayList<String>();
+       private Map<String, Float> floatMap = new HashMap<String, Float>();
+       private Map<SimpleObject, String> objectMap = new HashMap<SimpleObject, String>();
+       private Map<String, Map<String, String>> mapOfMaps = new HashMap<String, Map<String, String>>();
+
+       public String getId() {
+               return id;
+       }
+
+       public void setId(String id) {
+               this.id = id;
+       }
+
+       public String getLabel() {
+               return label;
+       }
+
+       public void setLabel(String label) {
+               this.label = label;
+       }
+
+       public SimpleObject getSimpleObject() {
+               return simpleObject;
+       }
+
+       public void setSimpleObject(SimpleObject simpleObject) {
+               this.simpleObject = simpleObject;
+       }
+
+       public List<String> getStringList() {
+               return stringList;
+       }
+
+       public void setStringList(List<String> stringList) {
+               this.stringList = stringList;
+       }
+
+       public Map<String, Float> getFloatMap() {
+               return floatMap;
+       }
+
+       public void setFloatMap(Map<String, Float> floatMap) {
+               this.floatMap = floatMap;
+       }
+
+       public Map<SimpleObject, String> getObjectMap() {
+               return objectMap;
+       }
+
+       public void setObjectMap(Map<SimpleObject, String> objectMap) {
+               this.objectMap = objectMap;
+       }
+
+       public Map<String, Map<String, String>> getMapOfMaps() {
+               return mapOfMaps;
+       }
+
+       public void setMapOfMaps(Map<String, Map<String, String>> mapOfMaps) {
+               this.mapOfMaps = mapOfMaps;
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/JxlDaoSupportTest.java b/trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/JxlDaoSupportTest.java
new file mode 100644 (file)
index 0000000..75e6b1c
--- /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.server.jxl.dao;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.springframework.core.io.ClassPathResource;
+
+@SuppressWarnings("restriction")
+public class JxlDaoSupportTest extends TestCase {
+       public void testBasic() throws Exception {
+               JxlDaoSupport jsd = new JxlDaoSupport();
+               jsd.getExternalRefs().put("test", new OtherObject());
+
+               jsd.getResources().add(new ClassPathResource("/dao/simple.xls"));
+               jsd.init();
+
+               SimpleObject soAaa = jsd.getByKey(SimpleObject.class, "aaa");
+               assertNotNull(soAaa);
+               assertEquals("aaa", soAaa.getString());
+               assertEquals(1, soAaa.getInteger().intValue());
+               assertNotNull(soAaa.getOtherObject());
+               assertEquals("USD", soAaa.getOtherObject().getKey());
+               assertEquals("US Dollar", soAaa.getOtherObject().getValue());
+
+               SimpleObject soBbb = jsd.getByKey(SimpleObject.class, "bbb");
+               assertNotNull(soBbb.getOtherObject());
+               assertEquals("bbb", ((SimpleObject) soBbb.getOtherObject().getValue())
+                               .getString());
+
+               List<SimpleObject> simpleObjects = jsd.list(SimpleObject.class, null);
+               assertEquals(4, simpleObjects.size());
+
+               List<CollectionsObject> collectionsObjects = jsd.list(
+                               CollectionsObject.class, null);
+               assertEquals(4, collectionsObjects.size());
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/OtherObject.java b/trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/OtherObject.java
new file mode 100644 (file)
index 0000000..ec41c55
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.jxl.dao;
+
+public class OtherObject {
+       private String key;
+       private Object value;
+
+       public String getKey() {
+               return key;
+       }
+
+       public void setKey(String key) {
+               this.key = key;
+       }
+
+       public Object getValue() {
+               return value;
+       }
+
+       public void setValue(Object value) {
+               this.value = value;
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/SimpleObject.java b/trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/SimpleObject.java
new file mode 100644 (file)
index 0000000..eee4ec5
--- /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.server.jxl.dao;
+
+public class SimpleObject {
+       private String string;
+       private Integer integer;
+       private OtherObject otherObject;
+       private OtherObject anotherObject;
+
+       public String getString() {
+               return string;
+       }
+
+       public void setString(String sting) {
+               this.string = sting;
+       }
+
+       public Integer getInteger() {
+               return integer;
+       }
+
+       public void setInteger(Integer integer) {
+               this.integer = integer;
+       }
+
+       public OtherObject getOtherObject() {
+               return otherObject;
+       }
+
+       public void setOtherObject(OtherObject otherObject) {
+               this.otherObject = otherObject;
+       }
+
+       public OtherObject getAnotherObject() {
+               return anotherObject;
+       }
+
+       public void setAnotherObject(OtherObject anotherObject) {
+               this.anotherObject = anotherObject;
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               return string.equals(((SimpleObject) obj).string);
+       }
+
+       @Override
+       public int hashCode() {
+               return string.hashCode();
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/SimpleObjectEditor.java b/trunk/server/runtime/org.argeo.server.jxl/src/test/java/org/argeo/server/jxl/dao/SimpleObjectEditor.java
new file mode 100644 (file)
index 0000000..c7cb00a
--- /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.server.jxl.dao;
+
+import java.beans.PropertyEditorSupport;
+
+public class SimpleObjectEditor extends PropertyEditorSupport {
+
+       @Override
+       public String getAsText() {
+               return ((SimpleObject) getValue()).getString();
+       }
+
+       @Override
+       public void setAsText(String text) throws IllegalArgumentException {
+               SimpleObject obj = new SimpleObject();
+               obj.setString(text);
+               setValue(obj);
+       }
+
+}
diff --git a/trunk/server/runtime/org.argeo.server.jxl/src/test/resources/dao/simple.xls b/trunk/server/runtime/org.argeo.server.jxl/src/test/resources/dao/simple.xls
new file mode 100644 (file)
index 0000000..6cb4c2e
Binary files /dev/null and b/trunk/server/runtime/org.argeo.server.jxl/src/test/resources/dao/simple.xls differ
diff --git a/trunk/server/runtime/org.argeo.server.jxl/src/test/resources/log4j.properties b/trunk/server/runtime/org.argeo.server.jxl/src/test/resources/log4j.properties
new file mode 100644 (file)
index 0000000..6a9667c
--- /dev/null
@@ -0,0 +1,13 @@
+log4j.rootLogger=WARN, console
+
+## Levels
+log4j.logger.org.argeo=DEBUG
+log4j.logger.org.argeo.server.jxl=TRACE
+
+## 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/trunk/server/runtime/org.argeo.server.modeshape/.classpath b/trunk/server/runtime/org.argeo.server.modeshape/.classpath
new file mode 100644 (file)
index 0000000..8cf7f48
--- /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.6"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/server/runtime/org.argeo.server.modeshape/.project b/trunk/server/runtime/org.argeo.server.modeshape/.project
new file mode 100644 (file)
index 0000000..3018b71
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.modeshape</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/trunk/server/runtime/org.argeo.server.modeshape/.settings/org.eclipse.jdt.core.prefs b/trunk/server/runtime/org.argeo.server.modeshape/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..5a7d6c7
--- /dev/null
@@ -0,0 +1,8 @@
+#Wed Feb 09 07:24:45 CET 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/server/runtime/org.argeo.server.modeshape/.settings/org.eclipse.pde.core.prefs b/trunk/server/runtime/org.argeo.server.modeshape/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..b6bd622
--- /dev/null
@@ -0,0 +1,4 @@
+#Wed Feb 09 07:24:45 CET 2011
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/trunk/server/runtime/org.argeo.server.modeshape/build.properties b/trunk/server/runtime/org.argeo.server.modeshape/build.properties
new file mode 100644 (file)
index 0000000..5fc538b
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .
diff --git a/trunk/server/runtime/org.argeo.server.modeshape/pom.xml b/trunk/server/runtime/org.argeo.server.modeshape/pom.xml
new file mode 100644 (file)
index 0000000..2e178ae
--- /dev/null
@@ -0,0 +1,71 @@
+<?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.server</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.modeshape</artifactId>
+       <name>Commons Server Modeshape</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Export-Package>
+                                                       org.argeo.modeshape.*,
+                                               </Export-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons.server</groupId>
+                       <artifactId>org.argeo.server.jcr</artifactId>
+                       <version>2.1.11</version>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.modeshape</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.jcr</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.dep.log4j</artifactId>
+                       <version>2.1.11</version>
+                       <type>pom</type>
+               </dependency>
+
+
+               <dependency>
+                       <groupId>org.argeo.commons.base</groupId>
+                       <artifactId>org.argeo.osgi.boot</artifactId>
+                       <version>2.1.11</version>
+                       <scope>test</scope>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.modeshape/src/main/java/org/argeo/modeshape/FileSystemRepository.java b/trunk/server/runtime/org.argeo.server.modeshape/src/main/java/org/argeo/modeshape/FileSystemRepository.java
new file mode 100644 (file)
index 0000000..ab008c5
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.modeshape;
+
+import java.util.UUID;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.argeo.jcr.JcrUtils;
+import org.modeshape.connector.filesystem.FileSystemSource;
+import org.modeshape.jcr.JcrConfiguration;
+import org.modeshape.jcr.JcrEngine;
+
+public class FileSystemRepository {
+       public void init() {
+               try {
+                       // Required in order to load mime type definitions
+                       Thread.currentThread().setContextClassLoader(JcrConfiguration.class.getClassLoader());
+                       JcrConfiguration config = new JcrConfiguration();
+                       config.repositorySource("fsSource")
+                                       .usingClass(FileSystemSource.class)
+                                       .setDescription("The repository for our content")
+                                       .setProperty("workspaceRootPath", "/home/mbaudier/tmp")
+                                       .setProperty("defaultWorkspaceName", "prod")
+                                       .setProperty("predefinedWorkspaceNames",
+                                                       new String[] { "staging", "dev" })
+                                       .setProperty(
+                                                       "rootNodeUuid",
+                                                       UUID.fromString("fd129c12-81a8-42ed-aa4b-820dba49e6f0"))
+                                       .setProperty("updatesAllowed", "true")
+                                       .setProperty("creatingWorkspaceAllowed", "false");
+                       config.repository("fsRepo").setSource("fsSource");
+
+                       JcrEngine jcrEngine = config.build();
+                       jcrEngine.start();
+                       Repository repository = jcrEngine.getRepository("fsRepo");
+                       Session session = repository.login(new SimpleCredentials("demo",
+                                       "demo".toCharArray()));
+                       JcrUtils.debug(session.getRootNode());
+               } catch (RepositoryException e) {
+                       e.printStackTrace();
+               }
+       }
+}
diff --git a/trunk/server/runtime/org.argeo.server.webextender/.classpath b/trunk/server/runtime/org.argeo.server.webextender/.classpath
new file mode 100644 (file)
index 0000000..8cf7f48
--- /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.6"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/trunk/server/runtime/org.argeo.server.webextender/.project b/trunk/server/runtime/org.argeo.server.webextender/.project
new file mode 100644 (file)
index 0000000..39a8ee8
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.server.webextender</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/trunk/server/runtime/org.argeo.server.webextender/.settings/org.eclipse.jdt.core.prefs b/trunk/server/runtime/org.argeo.server.webextender/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..606000e
--- /dev/null
@@ -0,0 +1,8 @@
+#Fri Jul 09 14:46:32 CEST 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/trunk/server/runtime/org.argeo.server.webextender/.settings/org.eclipse.pde.core.prefs b/trunk/server/runtime/org.argeo.server.webextender/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..c0af3ca
--- /dev/null
@@ -0,0 +1,4 @@
+#Fri Jul 09 14:46:32 CEST 2010
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/trunk/server/runtime/org.argeo.server.webextender/META-INF/spring/extender/extender.properties b/trunk/server/runtime/org.argeo.server.webextender/META-INF/spring/extender/extender.properties
new file mode 100644 (file)
index 0000000..4dde802
--- /dev/null
@@ -0,0 +1 @@
+undeploy.wars.at.shutdown=true
diff --git a/trunk/server/runtime/org.argeo.server.webextender/META-INF/spring/extender/webextender.xml b/trunk/server/runtime/org.argeo.server.webextender/META-INF/spring/extender/webextender.xml
new file mode 100644 (file)
index 0000000..2e45128
--- /dev/null
@@ -0,0 +1,21 @@
+<?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:osgi="http://www.springframework.org/schema/osgi"
+       xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans   
+     http://www.springframework.org/schema/beans/spring-beans.xsd
+     http://www.springframework.org/schema/osgi
+     http://www.springframework.org/schema/osgi/spring-osgi.xsd
+     http://www.springframework.org/schema/util
+     http://www.springframework.org/schema/util/spring-util.xsd">
+
+       <!-- we leave 5 mins to Tomcat to start -->
+       <osgi:reference id="tomcatServer" interface="org.apache.catalina.Service"
+               cardinality="0..1" timeout="300000" />
+
+       <bean id="warDeployer" class="org.argeo.server.webextender.TomcatDeployer"
+               p:service-ref="tomcatServer" />
+
+       <util:properties id="extenderProperties"
+               location="osgibundle:/META-INF/spring/extender/extender.properties" />
+</beans>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.webextender/build.properties b/trunk/server/runtime/org.argeo.server.webextender/build.properties
new file mode 100644 (file)
index 0000000..5fc538b
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .
diff --git a/trunk/server/runtime/org.argeo.server.webextender/pom.xml b/trunk/server/runtime/org.argeo.server.webextender/pom.xml
new file mode 100644 (file)
index 0000000..dd12da5
--- /dev/null
@@ -0,0 +1,71 @@
+<?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.server</groupId>
+               <artifactId>runtime</artifactId>
+               <version>2.1.11</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.server.webextender</artifactId>
+       <name>Commons Server Web Extender (Spring DM)</name>
+       <description>Configure / hack Spring DM web extender</description>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-source-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+
+                               <configuration>
+                                       <instructions>
+                                               <Fragment-Host>org.springframework.osgi.web.extender</Fragment-Host>
+                                               <Export-Package>org.argeo.server.webextender.*</Export-Package>
+                                               <Import-Package>org.springframework.beans.factory,*</Import-Package>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <!-- Spring -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.osgi.web</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.osgi.web.extender</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.apache.catalina</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.springframework.beans</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.eclipse.osgi</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>javax.servlet</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>slf4j.org.apache.commons.logging</artifactId>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/trunk/server/runtime/org.argeo.server.webextender/src/main/java/org/argeo/server/webextender/TomcatDeployer.java b/trunk/server/runtime/org.argeo.server.webextender/src/main/java/org/argeo/server/webextender/TomcatDeployer.java
new file mode 100644 (file)
index 0000000..192cead
--- /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.server.webextender;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Service;
+import org.springframework.osgi.web.deployer.WarDeployment;
+import org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer;
+import org.springframework.util.ObjectUtils;
+
+/**
+ * Wraps the Spring DM Tomcat deployer in order to avoid issue with call to
+ * getServerInfo() when undeployed. We need to hack a lot here because Spring
+ * OSGi Web is really not extendable.
+ */
+public class TomcatDeployer extends TomcatWarDeployer {
+       private String serverInfo;
+       private Service service;
+       private String contextPath = "/ui";
+
+       @Override
+       public void setService(Object service) {
+               this.service = (Service) service;
+               super.setService(service);
+               // TODO: listen to OSGi service so that we get notified in the
+               // (unlikely) case the underlying service is updated
+               serverInfo = ((Service) service).getInfo();
+               log.info("Enhanced Argeo Tomcat webapp deployer being used");
+       }
+
+       @Override
+       protected String getServerInfo() {
+               return serverInfo;
+       }
+
+       @Override
+       protected void startDeployment(WarDeployment deployment) throws Exception {
+               // Context context = ((TomcatWarDeployment)
+               // deployment).getCatalinaContext();
+               // context.setCookies(false);
+               super.startDeployment(deployment);
+
+               // Required for multiple RAP sessions to work with Tomcat
+               // see
+               // http://wiki.eclipse.org/RAP/FAQ#How_to_run_a_RAP_application_in_multiple_browser_tabs.2Fwindows.3F
+               Context context = getContext(contextPath);
+               if (context != null)
+                       context.setCookies(false);
+       }
+
+       /** @return null if not found */
+       private Context getContext(String path) {
+               for (Container container : getHost().findChildren()) {
+                       if (log.isTraceEnabled())
+                               log.trace(container.getClass() + ": " + container.getName());
+                       if (container instanceof Context) {
+                               Context context = (Context) container;
+                               if (path.equals(context.getPath()))
+                                       return context;
+                       }
+               }
+               return null;
+       }
+
+       private Container getHost() {
+               // get engine
+               Container container = service.getContainer();
+
+               if (container == null)
+                       throw new IllegalStateException(
+                                       "The Tomcat server doesn't have any Engines defined");
+               // now get host
+               Container[] children = container.findChildren();
+               if (ObjectUtils.isEmpty(children))
+                       throw new IllegalStateException(
+                                       "The Tomcat server doesn't have any Hosts defined");
+
+               // pick the first one and associate the context with it
+               return children[0];
+       }
+
+       public void setContextPath(String contextPath) {
+               this.contextPath = contextPath;
+       }
+
+}
diff --git a/trunk/server/runtime/pom.xml b/trunk/server/runtime/pom.xml
new file mode 100644 (file)
index 0000000..59c5bbe
--- /dev/null
@@ -0,0 +1,27 @@
+<?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">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <version>2.1.11</version>
+               <artifactId>server</artifactId>
+               <relativePath>..</relativePath>
+       </parent>
+       <groupId>org.argeo.commons.server</groupId>
+       <artifactId>runtime</artifactId>
+       <name>Commons Server Runtime</name>
+       <packaging>pom</packaging>
+       <modules>
+               <module>org.argeo.server.core</module>
+               <module>org.argeo.server.json</module>
+               <module>org.argeo.server.catalina.start</module>
+               <module>org.argeo.server.webextender</module>
+               <module>org.argeo.server.jxl</module>
+               <module>org.argeo.server.hibernate</module>
+               <module>org.argeo.server.ads</module>
+               <module>org.argeo.server.jcr</module>
+               <module>org.argeo.server.jcr.mvc</module>
+               <module>org.argeo.server.jackrabbit</module>
+               <module>org.argeo.server.modeshape</module>
+       </modules>
+</project>
\ No newline at end of file