Some more UI functionalities :
authorBruno Sinou <bsinou@argeo.org>
Thu, 28 Feb 2013 11:21:05 +0000 (11:21 +0000)
committerBruno Sinou <bsinou@argeo.org>
Thu, 28 Feb 2013 11:21:05 +0000 (11:21 +0000)
+ fetch wizard
+ add / remove distant repositories
+ some slight UI enhancements

git-svn-id: https://svn.argeo.org/slc/trunk@6093 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

16 files changed:
lib/org.argeo.slc.lib.repo/META-INF/spring/sync.xml
plugins/org.argeo.slc.client.ui.dist/META-INF/spring/commands.xml
plugins/org.argeo.slc.client.ui.dist/icons/checked.gif [new file with mode: 0644]
plugins/org.argeo.slc.client.ui.dist/icons/unchecked.gif [new file with mode: 0644]
plugins/org.argeo.slc.client.ui.dist/plugin.xml
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/DistImages.java
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/PrivilegedJob.java [new file with mode: 0644]
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/commands/AddRepository.java
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/commands/DeleteWorkspace.java
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/commands/RepoSyncCommand.java
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/commands/UnregisterRemoteRepo.java [new file with mode: 0644]
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/utils/ViewerUtils.java [new file with mode: 0644]
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/views/DistributionsView.java
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/wizards/ChooseRightsPage.java
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/wizards/FetchWizard.java [new file with mode: 0644]
runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/RepoSync.java

index a5869905176cbd901a2cbb5e84084c9c1058f863..b346e436f5e0f0328daead166c027c2c64f42c8a 100644 (file)
@@ -5,10 +5,10 @@
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\r
        http://www.argeo.org/schema/slc-flow http://www.argeo.org/schema/slc-flow-1.2.xsd">\r
 \r
-       <!-- Hello world -->\r
+       <!-- Sync from to repository -->\r
        <flow:flow name="sync">\r
                <flow:spec>\r
-                       <flow:primitive name="sourceRepo"\r
+                       <flow:primitive name="sourceRepoUri"\r
                                value="http://repo.argeo.org/data/pub/java" />\r
                        <flow:primitive name="sourceWksp" value="" />\r
                        <flow:primitive name="sourceUsername" value="${user.name}" />\r
                        <flow:primitive name="targetUsername" value="${user.name}" />\r
                        <flow:primitive name="targetPassword" type="password"\r
                                value="" />\r
-                       <flow:primitive name="targetRepo" value="vm:///java/" />\r
+                       <flow:primitive name="targetRepoUri" value="vm:///java/" />\r
                </flow:spec>\r
                <bean class="org.argeo.slc.repo.RepoSync">\r
                        <flow:variable proxy-target-class="false" />\r
-                       <property name="sourceRepo" value="@{sourceRepo}" />\r
+                       <property name="sourceRepoUri" value="@{sourceRepo}" />\r
                        <property name="sourceWksp" value="@{sourceWksp}" />\r
                        <property name="sourceUsername" value="@{sourceUsername}" />\r
                        <property name="sourcePassword" value="@{sourcePassword}" />\r
                        <property name="targetUsername" value="@{targetUsername}" />\r
                        <property name="targetPassword" value="@{targetPassword}" />\r
-                       <property name="targetRepo" value="@{targetRepo}" />\r
+                       <property name="targetRepoUri" value="@{targetRepo}" />\r
                        <property name="repositoryFactory" ref="repositoryFactory" />\r
                </bean>\r
        </flow:flow>\r
index 7ad2b6b6a467d9e5dfd44d7a3f7b713ad37be313..6a43fd6a58c2bcb07d79168298418b51c789cfb1 100644 (file)
                scope="prototype">
                <property name="repository" ref="javaRepository" />
        </bean>
+       <bean id="repoSyncCommand" class="org.argeo.slc.client.ui.dist.commands.RepoSyncCommand"
+               scope="prototype">
+               <property name="repositoryFactory" ref="repositoryFactory" />
+               <property name="nodeRepository" ref="nodeRepository" />
+               <property name="keyring" ref="keyring" />
+       </bean>
+       <bean id="unregisterRemoteRepo"
+               class="org.argeo.slc.client.ui.dist.commands.UnregisterRemoteRepo"
+               scope="prototype">
+               <property name="nodeRepository" ref="nodeRepository" />
+       </bean>
+
        <bean id="copyWorkspace" class="org.argeo.slc.client.ui.dist.commands.CopyWorkspace"
                scope="prototype">
                <property name="repository" ref="javaRepository" />
diff --git a/plugins/org.argeo.slc.client.ui.dist/icons/checked.gif b/plugins/org.argeo.slc.client.ui.dist/icons/checked.gif
new file mode 100644 (file)
index 0000000..00da14e
Binary files /dev/null and b/plugins/org.argeo.slc.client.ui.dist/icons/checked.gif differ
diff --git a/plugins/org.argeo.slc.client.ui.dist/icons/unchecked.gif b/plugins/org.argeo.slc.client.ui.dist/icons/unchecked.gif
new file mode 100644 (file)
index 0000000..7aa131a
Binary files /dev/null and b/plugins/org.argeo.slc.client.ui.dist/icons/unchecked.gif differ
index 3d8bc0e025b1796aced91054d2abc9956fbc80b9..eede0bcbbe9cbb76d8296a4845ea88b9d16e0bd5 100644 (file)
     <!-- Commands --> 
        <extension
          point="org.eclipse.ui.commands">
+               <!-- Command which do not need dependency injection -->
+               <command
+            defaultHandler="org.argeo.slc.client.ui.dist.commands.RefreshArtifactBrowser"
+            id="org.argeo.slc.client.ui.dist.refreshArtifactBrowser"
+            name="Refresh Artifact Browser">
+       </command>
                <command
             defaultHandler="org.argeo.slc.client.ui.dist.commands.ShowSizeColumn"
             id="org.argeo.slc.client.ui.dist.showSizeColumn"
                                </class>
                        </state>
        </command>
+
+               <!-- Command with Spring dependency injection -->
                <command
-            defaultHandler="org.argeo.slc.client.ui.dist.commands.RefreshArtifactBrowser"
-            id="org.argeo.slc.client.ui.dist.refreshArtifactBrowser"
-            name="Refresh Artifact Browser">
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            id="org.argeo.slc.client.ui.dist.unregisterRemoteRepo"
+            name="Unregister remote repository">
+            <commandParameter
+                       id="org.argeo.slc.client.ui.dist.repoNodePath"
+                       name="Remote Repo path">
+                       </commandParameter>
        </command>
+               
                <command
             id="org.argeo.slc.client.ui.dist.runInOSGi"
             defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
             defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
             name="Add Repository">
        </command>
+       
+       <command
+            id="org.argeo.slc.client.ui.dist.repoSyncCommand"
+            defaultHandler="org.argeo.eclipse.spring.SpringCommandHandler"
+            name="Fetch repository">
+            <commandParameter
+                       id="targetRepoPath"
+                       name="Target repo path">
+                       </commandParameter>
+       </command>
        
                <!-- TO MANIPULATE WORKSPACES -->
                <command
                 label="Add repository"
                 tooltip="Add a repository">
             </command>
-               <!--
+            <command
+               commandId="org.argeo.slc.client.ui.dist.refreshDistributionsView"
+                icon="icons/refresh.png"
+                label="Refresh"
+                tooltip="Refresh distributions view">
+            </command>
+           <!--
             <command
                commandId="org.argeo.slc.client.ui.dist.runInOSGi"
                 icon="icons/showSize.gif"
index 5ee185371082ed394b90afd55813694a75446e4f..f88b8a95eef4b181b4f8e2fc51b0ac5f3275a4f1 100644 (file)
@@ -25,4 +25,10 @@ public class DistImages {
                        .getImageDescriptor("icons/artifactVersionBase.gif").createImage();
        public final static Image IMG_FILE = DistPlugin.getImageDescriptor(
                        "icons/file.gif").createImage();
+
+       /** Icons to handle check boxes */
+       public final static Image CHECKED = DistPlugin.getImageDescriptor(
+                       "icons/checked.gif").createImage();
+       public final static Image UNCHECKED = DistPlugin.getImageDescriptor(
+                       "icons/unchecked.gif").createImage();
 }
diff --git a/plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/PrivilegedJob.java b/plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/PrivilegedJob.java
new file mode 100644 (file)
index 0000000..c10f472
--- /dev/null
@@ -0,0 +1,43 @@
+package org.argeo.slc.client.ui.dist;
+
+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
index 4d46022e022e5730d005600c14a685a821e4818d..723954854d2ffbe6f6b7c956747dcfeead9d46a4 100644 (file)
@@ -34,6 +34,7 @@ import org.argeo.jcr.ArgeoNames;
 import org.argeo.jcr.ArgeoTypes;
 import org.argeo.jcr.JcrUtils;
 import org.argeo.jcr.UserJcrUtils;
+import org.argeo.slc.client.ui.dist.DistPlugin;
 import org.argeo.slc.jcr.SlcNames;
 import org.argeo.slc.repo.RepoConstants;
 import org.argeo.util.security.Keyring;
@@ -64,6 +65,10 @@ import org.eclipse.swt.widgets.Text;
 public class AddRepository extends AbstractHandler implements ArgeoNames,
                SlcNames {
 
+       public final static String ID = DistPlugin.ID + ".addRepository";
+       public final static String DEFAULT_LABEL = "Register a repository";
+       public final static String DEFAULT_ICON_PATH = "icons/addItem.gif";
+       
        private RepositoryFactory repositoryFactory;
        private Repository nodeRepository;
        private Keyring keyring;
index f51aff1409c3f647a8fa8e462d7e370a1688054e..676a921c02bc503ea980c9ab68d87269f1d82074 100644 (file)
@@ -22,9 +22,8 @@ import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.nodetype.NodeType;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.slc.SlcException;
 import org.argeo.slc.client.ui.dist.DistPlugin;
 import org.argeo.slc.client.ui.dist.utils.CommandHelpers;
 import org.eclipse.core.commands.AbstractHandler;
@@ -37,7 +36,7 @@ import org.eclipse.jface.dialogs.MessageDialog;
  */
 
 public class DeleteWorkspace extends AbstractHandler {
-       private static final Log log = LogFactory.getLog(DeleteWorkspace.class);
+       // private static final Log log = LogFactory.getLog(DeleteWorkspace.class);
 
        public final static String ID = DistPlugin.ID + ".deleteWorkspace";
        public final static String PARAM_WORKSPACE_NAME = DistPlugin.ID
@@ -50,43 +49,53 @@ public class DeleteWorkspace extends AbstractHandler {
 
        public Object execute(ExecutionEvent event) throws ExecutionException {
 
-               // MessageDialog.openWarning(DistPlugin.getDefault()
-               // .getWorkbench().getDisplay().getActiveShell(),
-               // "WARNING", "Not yet implemented");
-               // return null;
-
                String workspaceName = event.getParameter(PARAM_WORKSPACE_NAME);
-               String msg = "Your are about to clear workspace [" + workspaceName
-                               + "].\n Do you really want to proceed ?";
+
+               String msg = "Your are about to completely delete workspace ["
+                               + workspaceName + "].\n Do you really want to proceed ?";
 
                boolean result = MessageDialog.openConfirm(DistPlugin.getDefault()
                                .getWorkbench().getDisplay().getActiveShell(),
-                               "Confirm workspace clear", msg);
+                               "Confirm workspace deletion", msg);
+
+               if (result) {
+                       // msg =
+                       // "There is no possible turning back, are your REALLY sure you want to proceed ?";
+                       msg = "WARNING: \nCurrent Jackrabbit version used does "
+                                       + "not support workspace management.\n"
+                                       + "Thus, the workspace will only be cleaned so "
+                                       + "that you can launch fetch process again.\n\n"
+                                       + "Do you still want to proceed ?";
+                       result = MessageDialog.openConfirm(DistPlugin.getDefault()
+                                       .getWorkbench().getDisplay().getActiveShell(),
+                                       "Confirm workspace deletion", msg);
+               }
+
                if (result) {
                        Session session = null;
                        try {
                                session = repository.login(workspaceName);
+                               // TODO use this with a newer version of Jackrabbit
+                               // Workspace wsp = session.getWorkspace();
+                               // wsp.deleteWorkspace(workspaceName);
+
                                NodeIterator nit = session.getRootNode().getNodes();
                                while (nit.hasNext()) {
                                        Node node = nit.nextNode();
                                        if (node.isNodeType(NodeType.NT_FOLDER)
                                                        || node.isNodeType(NodeType.NT_UNSTRUCTURED)) {
-                                               String path = node.getPath();
+                                               // String path = node.getPath();
                                                node.remove();
                                                session.save();
-                                               if (log.isDebugEnabled())
-                                                       log.debug("Cleared " + path + " in "
-                                                                       + workspaceName);
                                        }
                                }
                                CommandHelpers.callCommand(RefreshDistributionsView.ID);
                        } catch (RepositoryException re) {
-                               throw new ArgeoException(
+                               throw new SlcException(
                                                "Unexpected error while deleting workspace ["
                                                                + workspaceName + "].", re);
                        } finally {
-                               if (session != null)
-                                       session.logout();
+                               JcrUtils.logoutQuietly(session);
                        }
                }
                return null;
index c9ff452a36d803f5a136295d71346cc3e4e82028..e780cdf43d175a39e05af1c2fdacee5ac393a045 100644 (file)
@@ -1,34 +1,73 @@
 package org.argeo.slc.client.ui.dist.commands;
 
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
 import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
 
+import org.argeo.jcr.JcrUtils;
+import org.argeo.slc.SlcException;
 import org.argeo.slc.client.ui.dist.DistPlugin;
+import org.argeo.slc.client.ui.dist.utils.CommandHelpers;
+import org.argeo.slc.client.ui.dist.wizards.FetchWizard;
 import org.argeo.slc.repo.RepoSync;
+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.wizard.WizardDialog;
+import org.eclipse.ui.handlers.HandlerUtil;
 
 /** Wraps a {@link RepoSync} as an Eclipse command. */
 public class RepoSyncCommand extends AbstractHandler {
-       public final static String ID = DistPlugin.ID + ".repoSync";
-       public final static String PARAM_SOURCE_REPO = "sourceRepo";
-       public final static String PARAM_TARGET_REPO = "targetRepo";
-       public final static String DEFAULT_LABEL = "Repo sync";
+       // private final static Log log = LogFactory.getLog(RepoSyncCommand.class);
+
+       public final static String ID = DistPlugin.ID + ".repoSyncCommand";
+       public final static String PARAM_TARGET_REPO = "targetRepoPath";
+       public final static String DEFAULT_LABEL = "Fetch ...";
        public final static String DEFAULT_ICON_PATH = "icons/addItem.gif";
-       
+
+       // DEPENDENCY INJECTION
+       private Keyring keyring;
        private RepositoryFactory repositoryFactory;
+       private Repository nodeRepository;
+
+       private Session currSession;
 
        public Object execute(ExecutionEvent event) throws ExecutionException {
-               RepoSync repoSync = new RepoSync();
-               repoSync.setRepositoryFactory(repositoryFactory);
-               repoSync.setSourceRepo(event.getParameter(PARAM_SOURCE_REPO));
-               repoSync.setTargetRepo(event.getParameter(PARAM_TARGET_REPO));
-               repoSync.run();
-               return null;
+               try {
+                       currSession = nodeRepository.login();
+                       // Target Repository
+                       String targetRepoPath = event.getParameter(PARAM_TARGET_REPO);
+                       Node targetRepoNode = currSession.getNode(targetRepoPath);
+
+                       FetchWizard wizard = new FetchWizard(keyring, repositoryFactory,
+                                       nodeRepository);
+                       wizard.setTargetRepoNode(targetRepoNode);
+
+                       WizardDialog dialog = new WizardDialog(
+                                       HandlerUtil.getActiveShell(event), wizard);
+                       dialog.open();
+                       CommandHelpers.callCommand(RefreshDistributionsView.ID);
+                       return null;
+               } catch (RepositoryException e) {
+                       throw new SlcException("Unexpected error while fetching data", e);
+               } finally {
+                       JcrUtils.logoutQuietly(currSession);
+               }
        }
 
+       // DEPENDENCY INJECTION
        public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
                this.repositoryFactory = repositoryFactory;
        }
 
-}
+       public void setKeyring(Keyring keyring) {
+               this.keyring = keyring;
+       }
+
+       public void setNodeRepository(Repository repository) {
+               this.nodeRepository = repository;
+       }
+}
\ No newline at end of file
diff --git a/plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/commands/UnregisterRemoteRepo.java b/plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/commands/UnregisterRemoteRepo.java
new file mode 100644 (file)
index 0000000..1dfe913
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.slc.client.ui.dist.commands;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.slc.SlcException;
+import org.argeo.slc.client.ui.dist.DistPlugin;
+import org.argeo.slc.client.ui.dist.utils.CommandHelpers;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.MessageDialog;
+
+/**
+ * Unregisters a remote repository by deleting the corresponding RepoNode from
+ * the NodeRepository. It does not affect the repository instance
+ */
+
+public class UnregisterRemoteRepo extends AbstractHandler {
+       // private static final Log log = LogFactory
+       // .getLog(UnregisterRemoteRepo.class);
+       public final static String ID = DistPlugin.ID + ".unregisterRemoteRepo";
+       public final static String DEFAULT_LABEL = "Unregister repository";
+       public final static String DEFAULT_ICON_PATH = "icons/removeItem.gif";
+       public final static String PARAM_REPO_PATH = DistPlugin.ID
+                       + ".repoNodePath";
+
+       // DEPENCY INJECTION
+       private Repository nodeRepository;
+
+       public Object execute(ExecutionEvent event) throws ExecutionException {
+               Session session = null;
+               String repoPath = event.getParameter(PARAM_REPO_PATH);
+               if (repoPath == null)
+                       return null;
+
+               try {
+                       session = nodeRepository.login();
+                       Node rNode = session.getNode(repoPath);
+                       if (rNode.isNodeType(ArgeoTypes.ARGEO_REMOTE_REPOSITORY)) {
+
+                               String alias = rNode.getProperty(Property.JCR_TITLE)
+                                               .getString();
+                               String msg = "Your are about to unregister remote repository: "
+                                               + alias + "\n" + "Are you sure you want to proceed ?";
+
+                               boolean result = MessageDialog.openConfirm(DistPlugin
+                                               .getDefault().getWorkbench().getDisplay()
+                                               .getActiveShell(), "Confirm Delete", msg);
+
+                               if (result) {
+                                       rNode.remove();
+                                       session.save();
+                               }
+                               CommandHelpers.callCommand(RefreshDistributionsView.ID);
+                       }
+
+                       // } catch (Exception e) {
+               } catch (RepositoryException e) {
+                       throw new SlcException(
+                                       "Unexpected error while deleting artifacts.", e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+               }
+               return null;
+       }
+
+       // DEPENCY INJECTION
+       public void setNodeRepository(Repository nodeRepository) {
+               this.nodeRepository = nodeRepository;
+       }
+}
\ No newline at end of file
diff --git a/plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/utils/ViewerUtils.java b/plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/utils/ViewerUtils.java
new file mode 100644 (file)
index 0000000..18f77f3
--- /dev/null
@@ -0,0 +1,56 @@
+package org.argeo.slc.client.ui.dist.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 table to display nodes list.
+ */
+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;
+       }
+}
index 03461829141f88f2d3c086489be2409dc7e72a0a..dc37c34205837f0a882cd35a243723625e337c8e 100644 (file)
@@ -46,11 +46,14 @@ import org.argeo.jcr.JcrUtils;
 import org.argeo.jcr.UserJcrUtils;
 import org.argeo.slc.SlcException;
 import org.argeo.slc.client.ui.dist.DistPlugin;
+import org.argeo.slc.client.ui.dist.commands.AddRepository;
 import org.argeo.slc.client.ui.dist.commands.CopyWorkspace;
 import org.argeo.slc.client.ui.dist.commands.CreateWorkspace;
 import org.argeo.slc.client.ui.dist.commands.DeleteWorkspace;
 import org.argeo.slc.client.ui.dist.commands.ManageWorkspaceAuth;
 import org.argeo.slc.client.ui.dist.commands.NormalizeDistribution;
+import org.argeo.slc.client.ui.dist.commands.RepoSyncCommand;
+import org.argeo.slc.client.ui.dist.commands.UnregisterRemoteRepo;
 import org.argeo.slc.client.ui.dist.editors.DistributionEditor;
 import org.argeo.slc.client.ui.dist.editors.DistributionEditorInput;
 import org.argeo.slc.client.ui.dist.utils.CommandHelpers;
@@ -126,6 +129,8 @@ public class DistributionsView extends ViewPart implements SlcNames, ArgeoNames
 
                viewer.setContentProvider(new DistributionsContentProvider());
                viewer.addDoubleClickListener(new DistributionsDCL());
+               // Enable selection retrieving from outside the view
+               getSite().setSelectionProvider(viewer);
 
                // Drag'n drop
                Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
@@ -175,65 +180,107 @@ public class DistributionsView extends ViewPart implements SlcNames, ArgeoNames
                }
 
                viewer.setInput(nodeRepository);
-
        }
 
        /** Programatically configure the context menu */
        protected void contextMenuAboutToShow(IMenuManager menuManager) {
                IWorkbenchWindow window = DistPlugin.getDefault().getWorkbench()
                                .getActiveWorkbenchWindow();
-               // Get Current selected item :
-               Object firstElement = ((IStructuredSelection) viewer.getSelection())
-                               .getFirstElement();
+               try {
+                       // Most of the implemented commands support only one selected
+                       // element
+                       boolean singleElement = ((IStructuredSelection) viewer
+                                       .getSelection()).size() == 1;
+                       // Get Current selected item :
+                       Object firstElement = ((IStructuredSelection) viewer.getSelection())
+                                       .getFirstElement();
 
-               if (firstElement instanceof TreeParent
-                               || firstElement instanceof RepoElem) {
-                       String wsName = null;
+                       if (firstElement instanceof TreeParent
+                                       || firstElement instanceof RepoElem) {
+                               String wsName = null;
+                               String targetRepoPath = null;
 
-                       // Build conditions depending on element type (repo or distribution
-                       // for the time being)
-                       boolean isDistribElem = false; // , isRepoElem = false;
+                               // Build conditions depending on element type (repo or
+                               // distribution
+                               // for the time being)
+                               boolean isDistribElem = false; // , isRepoElem = false;
 
-                       if (firstElement instanceof DistributionElem) {
-                               isDistribElem = true;
-                               wsName = ((DistributionElem) firstElement).getName();
-                       }
+                               if (firstElement instanceof DistributionElem) {
+                                       isDistribElem = true;
+                                       wsName = ((DistributionElem) firstElement).getName();
+                               }
 
-                       // create workspace
-                       CommandHelpers.refreshCommand(menuManager, window,
-                                       CreateWorkspace.ID, CreateWorkspace.DEFAULT_LABEL,
-                                       CreateWorkspace.DEFAULT_ICON_PATH, !isDistribElem);
-
-                       // Normalize workspace
-                       Map<String, String> params = new HashMap<String, String>();
-                       params.put(NormalizeDistribution.PARAM_WORKSPACE, wsName);
-                       CommandHelpers.refreshParameterizedCommand(menuManager, window,
-                                       NormalizeDistribution.ID,
-                                       NormalizeDistribution.DEFAULT_LABEL,
-                                       NormalizeDistribution.DEFAULT_ICON_PATH, isDistribElem,
-                                       params);
-
-                       // Copy workspace
-                       params = new HashMap<String, String>();
-                       params.put(CopyWorkspace.PARAM_WORKSPACE_NAME, wsName);
-                       CommandHelpers.refreshParameterizedCommand(menuManager, window,
-                                       CopyWorkspace.ID, CopyWorkspace.DEFAULT_LABEL,
-                                       CopyWorkspace.DEFAULT_ICON_PATH, isDistribElem, params);
-
-                       // Delete Workspace
-                       params = new HashMap<String, String>();
-                       params.put(DeleteWorkspace.PARAM_WORKSPACE_NAME, wsName);
-                       CommandHelpers.refreshParameterizedCommand(menuManager, window,
-                                       DeleteWorkspace.ID, DeleteWorkspace.DEFAULT_LABEL,
-                                       DeleteWorkspace.DEFAULT_ICON_PATH, isDistribElem, params);
-
-                       // Manage workspace authorizations
-                       params = new HashMap<String, String>();
-                       params.put(ManageWorkspaceAuth.PARAM_WORKSPACE_NAME, wsName);
-                       CommandHelpers.refreshParameterizedCommand(menuManager, window,
-                                       ManageWorkspaceAuth.ID, ManageWorkspaceAuth.DEFAULT_LABEL,
-                                       ManageWorkspaceAuth.DEFAULT_ICON_PATH, isDistribElem,
-                                       params);
+                               if (firstElement instanceof RepoElem) {
+                                       Node node = ((RepoElem) firstElement).getRepoNode();
+                                       targetRepoPath = node.getPath();
+                               }
+
+                               // create workspace
+                               CommandHelpers.refreshCommand(menuManager, window,
+                                               CreateWorkspace.ID, CreateWorkspace.DEFAULT_LABEL,
+                                               CreateWorkspace.DEFAULT_ICON_PATH, !isDistribElem
+                                                               && singleElement);
+
+                               // Register a remote repository
+                               CommandHelpers.refreshCommand(menuManager, window,
+                                               AddRepository.ID, AddRepository.DEFAULT_LABEL,
+                                               AddRepository.DEFAULT_ICON_PATH, !isDistribElem
+                                                               && singleElement);
+
+                               // Unregister a remote repository
+                               Map<String, String> params = new HashMap<String, String>();
+                               params.put(UnregisterRemoteRepo.PARAM_REPO_PATH, targetRepoPath);
+                               CommandHelpers.refreshParameterizedCommand(menuManager, window,
+                                               UnregisterRemoteRepo.ID,
+                                               UnregisterRemoteRepo.DEFAULT_LABEL,
+                                               UnregisterRemoteRepo.DEFAULT_ICON_PATH, !isDistribElem
+                                                               && singleElement, params);
+
+                               // Fetch repository
+                               params = new HashMap<String, String>();
+                               params.put(RepoSyncCommand.PARAM_TARGET_REPO, targetRepoPath);
+                               CommandHelpers.refreshParameterizedCommand(menuManager, window,
+                                               RepoSyncCommand.ID, RepoSyncCommand.DEFAULT_LABEL,
+                                               RepoSyncCommand.DEFAULT_ICON_PATH, !isDistribElem
+                                                               && singleElement, params);
+
+                               // Normalize workspace
+                               params = new HashMap<String, String>();
+                               params.put(NormalizeDistribution.PARAM_WORKSPACE, wsName);
+                               CommandHelpers.refreshParameterizedCommand(menuManager, window,
+                                               NormalizeDistribution.ID,
+                                               NormalizeDistribution.DEFAULT_LABEL,
+                                               NormalizeDistribution.DEFAULT_ICON_PATH, isDistribElem
+                                                               && singleElement, params);
+
+                               // Copy workspace
+                               params = new HashMap<String, String>();
+                               params.put(CopyWorkspace.PARAM_WORKSPACE_NAME, wsName);
+                               CommandHelpers.refreshParameterizedCommand(menuManager, window,
+                                               CopyWorkspace.ID, CopyWorkspace.DEFAULT_LABEL,
+                                               CopyWorkspace.DEFAULT_ICON_PATH, isDistribElem
+                                                               && singleElement, params);
+
+                               // Delete Workspace
+                               params = new HashMap<String, String>();
+                               params.put(DeleteWorkspace.PARAM_WORKSPACE_NAME, wsName);
+                               CommandHelpers.refreshParameterizedCommand(menuManager, window,
+                                               DeleteWorkspace.ID, DeleteWorkspace.DEFAULT_LABEL,
+                                               DeleteWorkspace.DEFAULT_ICON_PATH, isDistribElem
+                                                               && singleElement, params);
+
+                               // Manage workspace authorizations
+                               params = new HashMap<String, String>();
+                               params.put(ManageWorkspaceAuth.PARAM_WORKSPACE_NAME, wsName);
+                               CommandHelpers.refreshParameterizedCommand(menuManager, window,
+                                               ManageWorkspaceAuth.ID,
+                                               ManageWorkspaceAuth.DEFAULT_LABEL,
+                                               ManageWorkspaceAuth.DEFAULT_ICON_PATH, isDistribElem
+                                                               && singleElement, params);
+                       }
+               } catch (RepositoryException e) {
+                       throw new SlcException("unexpected errror while "
+                                       + "building context menu", e);
                }
        }
 
@@ -242,9 +289,6 @@ public class DistributionsView extends ViewPart implements SlcNames, ArgeoNames
                viewer.getTree().setFocus();
        }
 
-       /*
-        * DEPENDENCY INJECTION
-        */
        /**
         * Force refresh of the whole view
         */
@@ -252,18 +296,6 @@ public class DistributionsView extends ViewPart implements SlcNames, ArgeoNames
                viewer.setContentProvider(new DistributionsContentProvider());
        }
 
-       public void setNodeRepository(Repository repository) {
-               this.nodeRepository = repository;
-       }
-
-       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
-               this.repositoryFactory = repositoryFactory;
-       }
-
-       public void setKeyring(Keyring keyring) {
-               this.keyring = keyring;
-       }
-
        /*
         * INTERNAL CLASSES
         */
@@ -642,4 +674,19 @@ public class DistributionsView extends ViewPart implements SlcNames, ArgeoNames
                        }
                }
        }
+
+       /*
+        * DEPENDENCY INJECTION
+        */
+       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
+               this.repositoryFactory = repositoryFactory;
+       }
+
+       public void setKeyring(Keyring keyring) {
+               this.keyring = keyring;
+       }
+
+       public void setNodeRepository(Repository repository) {
+               this.nodeRepository = repository;
+       }
 }
\ No newline at end of file
index 88bfc6a556525a57eac850e2ff00bb45429a5534..2c4f477fba7c167b78cd295d309c860016058ed1 100644 (file)
@@ -15,8 +15,6 @@
  */
 package org.argeo.slc.client.ui.dist.wizards;
 
-import java.util.regex.Pattern;
-
 import javax.jcr.security.Privilege;
 
 import org.eclipse.jface.wizard.WizardPage;
@@ -37,7 +35,7 @@ public class ChooseRightsPage extends WizardPage implements ModifyListener {
        private Combo authorizationCmb;
 
        // Define acceptable chars for the technical name
-       private static Pattern p = Pattern.compile("^[A-Za-z0-9]+$");
+       // private static Pattern p = Pattern.compile("^[A-Za-z0-9]+$");
 
        // USABLE SHORTCUTS
        protected final static String[] validAuthType = { Privilege.JCR_READ,
@@ -45,7 +43,7 @@ public class ChooseRightsPage extends WizardPage implements ModifyListener {
 
        public ChooseRightsPage() {
                super("Main");
-               setTitle("Manange authorizations on the current workspace");
+               setTitle("Manage authorizations on the current workspace");
        }
 
        public void createControl(Composite parent) {
@@ -82,9 +80,9 @@ public class ChooseRightsPage extends WizardPage implements ModifyListener {
                return authorizationCmb.getItem(authorizationCmb.getSelectionIndex());
        }
 
-       private static boolean match(String s) {
-               return p.matcher(s).matches();
-       }
+       // private static boolean match(String s) {
+       // return p.matcher(s).matches();
+       // }
 
        public void modifyText(ModifyEvent event) {
                String message = checkComplete();
diff --git a/plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/wizards/FetchWizard.java b/plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/wizards/FetchWizard.java
new file mode 100644 (file)
index 0000000..a3d86fc
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.slc.client.ui.dist.wizards;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+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.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+
+import org.argeo.ArgeoMonitor;
+import org.argeo.eclipse.ui.EclipseArgeoMonitor;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.slc.SlcException;
+import org.argeo.slc.client.ui.dist.DistImages;
+import org.argeo.slc.client.ui.dist.DistPlugin;
+import org.argeo.slc.client.ui.dist.PrivilegedJob;
+import org.argeo.slc.client.ui.dist.utils.ViewerUtils;
+import org.argeo.slc.repo.RepoConstants;
+import org.argeo.slc.repo.RepoSync;
+import org.argeo.slc.repo.RepoUtils;
+import org.argeo.util.security.Keyring;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.MessageDialog;
+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.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+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.Table;
+
+/**
+ * Defines parameters for the fetch process and run it using a {@link RepoSync}
+ * object.
+ */
+public class FetchWizard extends Wizard {
+
+       // Business objects
+       private Keyring keyring;
+       private RepositoryFactory repositoryFactory;
+       private Session currSession;
+
+       // Caches the workspace list
+       private List<String> wkspToSync = new ArrayList<String>();
+
+       private TableViewer wkspViewer;
+
+       private Node targetRepoNode, sourceRepoNode;
+
+       // This page widget
+       private DefineModelPage page;
+
+       public FetchWizard(Keyring keyring, RepositoryFactory repositoryFactory,
+                       Repository nodeRepository) {
+               super();
+               this.keyring = keyring;
+               this.repositoryFactory = repositoryFactory;
+               try {
+                       currSession = nodeRepository.login();
+               } catch (RepositoryException e) {
+                       throw new SlcException(
+                                       "Unexpected error while initializing fetch wizard", e);
+               }
+       }
+
+       @Override
+       public void dispose() {
+               JcrUtils.logoutQuietly(currSession);
+       }
+
+       @Override
+       public void addPages() {
+               try {
+                       page = new DefineModelPage();
+                       addPage(page);
+                       setWindowTitle("Fetch ...");
+               } catch (Exception e) {
+                       throw new SlcException("Cannot add page to wizard ", e);
+               }
+       }
+
+       @Override
+       public boolean performFinish() {
+               if (!canFinish())
+                       return false;
+               try {
+                       // Target Repository
+                       String targetRepoUri = targetRepoNode.getProperty(
+                                       ArgeoNames.ARGEO_URI).getString();
+                       Repository targetRepo = RepoUtils.getRepository(repositoryFactory,
+                                       keyring, targetRepoNode);
+                       Credentials targetCredentials = RepoUtils.getRepositoryCredentials(
+                                       keyring, targetRepoNode);
+
+                       // Source Repository
+                       String sourceRepoUri = sourceRepoNode.getProperty(
+                                       ArgeoNames.ARGEO_URI).getString();
+                       Repository sourceRepo = RepoUtils.getRepository(repositoryFactory,
+                                       keyring, sourceRepoNode);
+                       Credentials sourceCredentials = RepoUtils.getRepositoryCredentials(
+                                       keyring, sourceRepoNode);
+
+                       String msg = "Your are about to fetch data from repository: \n\t"
+                                       + sourceRepoUri + "\ninto target repository: \n\t"
+                                       + targetRepoUri + "\nDo you really want to proceed ?";
+
+                       boolean result = MessageDialog.openConfirm(DistPlugin.getDefault()
+                                       .getWorkbench().getDisplay().getActiveShell(),
+                                       "Confirm Fetch clear", msg);
+
+                       if (result) {
+                               RepoSync repoSync = new RepoSync(sourceRepo, sourceCredentials,
+                                               targetRepo, targetCredentials);
+                               repoSync.setTargetRepoUri(targetRepoUri);
+                               repoSync.setSourceRepoUri(sourceRepoUri);
+
+                               // / Specify workspaces to synchronise
+                               if (wkspToSync != null && wkspToSync.size() > 0)
+                                       repoSync.setSourceWkspList(wkspToSync);
+
+                               FetchJob job = new FetchJob(repoSync);
+                               job.setUser(true);
+                               job.schedule();
+                       }
+
+               } catch (Exception e) {
+                       throw new SlcException(
+                                       "Unexpected error while launching the fetch", e);
+               }
+               return true;
+       }
+
+       private class DefineModelPage extends WizardPage {
+
+               // This page widget
+               private Combo chooseSourceRepoCmb;
+
+               // Business objects
+               private Map<String, Node> sourceReposMap;
+
+               public DefineModelPage() {
+                       super("Main");
+                       setTitle("Define fetch parameters");
+               }
+
+               public void createControl(Composite parent) {
+
+                       // main layout
+                       Composite composite = new Composite(parent, SWT.NONE);
+                       composite.setLayout(new GridLayout(2, false));
+
+                       // Choose source repo
+                       new Label(composite, SWT.NONE)
+                                       .setText("Choose a source repository");
+                       chooseSourceRepoCmb = new Combo(composite, SWT.BORDER
+                                       | SWT.V_SCROLL);
+                       chooseSourceRepoCmb.setItems(getSourceRepoUris());
+                       GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+                       chooseSourceRepoCmb.setLayoutData(gd);
+
+                       // Workspace table
+                       Composite wkspTable = new Composite(composite, SWT.NONE);
+                       gd = new GridData();
+                       gd.horizontalSpan = 2;
+                       gd.grabExcessVerticalSpace = true;
+                       gd.verticalAlignment = SWT.FILL;
+                       wkspTable.setLayoutData(gd);
+                       wkspTable.setLayout(new GridLayout(1, false));
+                       addFilesTablePart(wkspTable);
+
+                       chooseSourceRepoCmb.addModifyListener(new ModifyListener() {
+                               public void modifyText(ModifyEvent e) {
+                                       String chosenUri = chooseSourceRepoCmb
+                                                       .getItem(chooseSourceRepoCmb.getSelectionIndex());
+                                       sourceRepoNode = sourceReposMap.get(chosenUri);
+                                       wkspViewer.setInput(sourceRepoNode);
+                               }
+                       });
+
+                       // initialize to first avalaible repo
+                       chooseSourceRepoCmb.select(0);
+
+                       // Compulsory
+                       setControl(composite);
+               }
+
+               // Helper to populate avalaible source repo list
+               protected String[] getSourceRepoUris() {
+                       try {
+                               Node repoList = currSession.getNode(UserJcrUtils.getUserHome(
+                                               currSession).getPath()
+                                               + RepoConstants.REPOSITORIES_BASE_PATH);
+
+                               String targetRepoUri = null;
+                               if (targetRepoNode != null) {
+                                       targetRepoUri = targetRepoNode.getProperty(
+                                                       ArgeoNames.ARGEO_URI).getString();
+                               }
+                               NodeIterator ni = repoList.getNodes();
+                               List<String> sourceRepoNames = new ArrayList<String>();
+                               // caches a map of the source repo nodes with their URI as a key
+                               // to ease further processing
+                               sourceReposMap = new HashMap<String, Node>();
+                               while (ni.hasNext()) {
+                                       Node currNode = ni.nextNode();
+                                       if (currNode.isNodeType(ArgeoTypes.ARGEO_REMOTE_REPOSITORY)) {
+                                               String currUri = currNode.getProperty(
+                                                               ArgeoNames.ARGEO_URI).getString();
+                                               if (targetRepoUri == null
+                                                               || !targetRepoUri.equals(currUri)) {
+                                                       sourceReposMap.put(currUri, currNode);
+                                                       sourceRepoNames.add(currUri);
+                                               }
+                                       }
+                               }
+                               return sourceRepoNames.toArray(new String[sourceRepoNames
+                                               .size()]);
+                       } catch (RepositoryException e) {
+                               throw new SlcException("Error while getting repo aliases", e);
+                       }
+               }
+
+               // Create the workspaces table
+               private void addFilesTablePart(Composite parent) {
+
+                       final Table table = new Table(parent, SWT.NONE | SWT.H_SCROLL
+                                       | SWT.V_SCROLL | SWT.BORDER);
+                       table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true));
+                       table.setLinesVisible(true);
+                       table.setHeaderVisible(true);
+                       wkspViewer = new TableViewer(table);
+
+                       // CHECKBOX COLUMN
+                       TableViewerColumn column = ViewerUtils.createTableViewerColumn(
+                                       wkspViewer, "", SWT.NONE, 20);
+                       column.setLabelProvider(new ColumnLabelProvider() {
+                               public String getText(Object element) {
+                                       return null;
+                               }
+
+                               public Image getImage(Object element) {
+                                       return wkspToSync.contains(element) ? DistImages.CHECKED
+                                                       : DistImages.UNCHECKED;
+                               }
+                       });
+                       column.setEditingSupport(new CheckboxEditingSupport(wkspViewer));
+                       // add select all option
+                       column.getColumn().addSelectionListener(new SelectionListener() {
+                               public void widgetSelected(SelectionEvent e) {
+                                       if (wkspToSync.size() > 0)
+                                               wkspToSync = new ArrayList<String>();
+                                       else {
+                                               String[] elements = (String[]) ((IStructuredContentProvider) wkspViewer
+                                                               .getContentProvider()).getElements(null);
+                                               wkspToSync = Arrays.asList(elements);
+                                       }
+                                       wkspViewer.refresh();
+                               }
+
+                               public void widgetDefaultSelected(SelectionEvent e) {
+                               }
+                       });
+
+                       // WORKSPACES COLUMN
+                       column = ViewerUtils.createTableViewerColumn(wkspViewer,
+                                       "Workspaces", SWT.NONE, 400);
+                       column.setLabelProvider(new ColumnLabelProvider());
+
+                       wkspViewer.setContentProvider(new IStructuredContentProvider() {
+                               // caches current repo
+                               private Repository currSourceRepo;
+                               private Credentials currSourceCred;
+
+                               public void inputChanged(Viewer viewer, Object oldInput,
+                                               Object newInput) {
+                                       // update current used repository
+                                       currSourceRepo = RepoUtils.getRepository(repositoryFactory,
+                                                       keyring, (Node) newInput);
+                                       currSourceCred = RepoUtils.getRepositoryCredentials(
+                                                       keyring, (Node) newInput);
+                                       // reset workspace list
+                                       wkspToSync = new ArrayList<String>();
+                               }
+
+                               public void dispose() {
+                               }
+
+                               public Object[] getElements(Object obj) {
+                                       Session session = null;
+                                       try {
+                                               session = currSourceRepo.login(currSourceCred);
+                                               return session.getWorkspace()
+                                                               .getAccessibleWorkspaceNames();
+                                       } catch (RepositoryException e) {
+                                               throw new SlcException(
+                                                               "Unexpected error while initializing fetch wizard",
+                                                               e);
+                                       } finally {
+                                               JcrUtils.logoutQuietly(session);
+                                       }
+
+                               }
+                       });
+               }
+
+               /** Select which file to import by editing a checkbox */
+               protected class CheckboxEditingSupport extends EditingSupport {
+
+                       private final TableViewer viewer;
+
+                       public CheckboxEditingSupport(TableViewer viewer) {
+                               super(viewer);
+                               this.viewer = viewer;
+                       }
+
+                       @Override
+                       protected boolean canEdit(Object element) {
+                               return true;
+                       }
+
+                       @Override
+                       protected CellEditor getCellEditor(Object element) {
+                               return new CheckboxCellEditor(null, SWT.CHECK | SWT.READ_ONLY);
+                       }
+
+                       @Override
+                       protected Object getValue(Object element) {
+                               return wkspToSync.contains(element);
+                       }
+
+                       @Override
+                       protected void setValue(Object element, Object value) {
+                               if ((Boolean) value && !wkspToSync.contains(element))
+                                       wkspToSync.add((String) element);
+                               else if (!(Boolean) value && wkspToSync.contains(element))
+                                       wkspToSync.remove(element);
+                               viewer.refresh();
+                       }
+               }
+       }
+
+       /**
+        * Define the privileged job that will be run asynchronously to accomplish
+        * the sync
+        */
+       private class FetchJob extends PrivilegedJob {
+               private RepoSync repoSync;
+
+               public FetchJob(RepoSync repoSync) {
+                       super("Fetch");
+                       this.repoSync = repoSync;
+               }
+
+               @Override
+               protected IStatus doRun(IProgressMonitor progressMonitor) {
+                       try {
+                               ArgeoMonitor monitor = new EclipseArgeoMonitor(progressMonitor);
+                               repoSync.setMonitor(monitor);
+                               repoSync.run();
+                       } catch (Exception e) {
+                               return new Status(IStatus.ERROR, DistPlugin.ID,
+                                               "Cannot fetch repository", e);
+                       }
+                       return Status.OK_STATUS;
+               }
+       }
+
+       public void setTargetRepoNode(Node targetRepoNode) {
+               this.targetRepoNode = targetRepoNode;
+       }
+
+       public void setSourceRepoNode(Node sourceRepoNode) {
+               this.sourceRepoNode = sourceRepoNode;
+       }
+
+       // private class FetchJob extends Job {
+       // private RepoSync repoSync;
+       // private final Authentication authentication;
+       // private Subject subject;
+       //
+       // public FetchJob(RepoSync repoSync) {
+       // super("Fetch");
+       // this.repoSync = repoSync;
+       // 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() {
+       // try {
+       // // SecurityContextHolder.setContext(securityContext);
+       // SecurityContextHolder.getContext().setAuthentication(
+       // authentication);
+       // ArgeoMonitor monitor = new EclipseArgeoMonitor(
+       // progressMonitor);
+       // repoSync.setMonitor(monitor);
+       // repoSync.run();
+       // } catch (Exception e) {
+       // return new Status(IStatus.ERROR, DistPlugin.ID,
+       // "Cannot fetch repository", e);
+       // }
+       // return Status.OK_STATUS;
+       // }
+       //
+       // };
+       // return Subject.doAs(subject, privilegedAction);
+       // }
+       // }
+}
\ No newline at end of file
index 3bcea12283a703bfc2a2d53f31ecf30d30e2f2b0..eaddaf690adcf5b80e799cb1da739eacb917c7f9 100644 (file)
  */
 package org.argeo.slc.repo;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.TimeZone;
 
@@ -33,9 +36,12 @@ import javax.jcr.RepositoryFactory;
 import javax.jcr.Session;
 import javax.jcr.SimpleCredentials;
 import javax.jcr.nodetype.NodeType;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoMonitor;
 import org.argeo.jcr.ArgeoJcrUtils;
 import org.argeo.jcr.JcrUtils;
 import org.argeo.slc.SlcException;
@@ -46,56 +52,104 @@ import org.xml.sax.SAXException;
 public class RepoSync implements Runnable {
        private final static Log log = LogFactory.getLog(RepoSync.class);
 
-       private final Calendar zero;
+       // Centralizes definition of workspaces that must be ignored by the sync.
+       private final static List<String> IGNORED_WSKP_LIST = Arrays.asList(
+                       "security", "localrepo");
 
-       private String sourceRepo;
-       private String targetRepo;
+       private final Calendar zero;
+       private Session sourceDefaultSession = null;
+       private Session targetDefaultSession = null;
 
-       private String sourceWksp;
+       private Repository sourceRepository;
+       private Credentials sourceCredentials;
+       private Repository targetRepository;
+       private Credentials targetCredentials;
 
+       // if Repository and Credentials objects are not explicitly set
+       private String sourceRepoUri;
        private String sourceUsername;
        private char[] sourcePassword;
+       private String targetRepoUri;
        private String targetUsername;
        private char[] targetPassword;
 
        private RepositoryFactory repositoryFactory;
 
+       private ArgeoMonitor monitor;
+       private List<String> sourceWkspList;
+
        public RepoSync() {
                zero = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
                zero.setTimeInMillis(0);
        }
 
+       /**
+        * 
+        * Shortcut to instantiate a RepoSync with already known repositories and
+        * credentials.
+        * 
+        * @param sourceRepository
+        * @param sourceCredentials
+        * @param targetRepository
+        * @param targetCredentials
+        */
+       public RepoSync(Repository sourceRepository, Credentials sourceCredentials,
+                       Repository targetRepository, Credentials targetCredentials) {
+               this();
+               this.sourceRepository = sourceRepository;
+               this.sourceCredentials = sourceCredentials;
+               this.targetRepository = targetRepository;
+               this.targetCredentials = targetCredentials;
+       }
+
        public void run() {
-               Session sourceDefaultSession = null;
-               Session targetDefaultSession = null;
                try {
                        long begin = System.currentTimeMillis();
 
-                       Repository sourceRepository = ArgeoJcrUtils.getRepositoryByUri(
-                                       repositoryFactory, sourceRepo);
-                       Repository targetRepository = ArgeoJcrUtils.getRepositoryByUri(
-                                       repositoryFactory, targetRepo);
-                       Credentials sourceCredentials = null;
-                       if (sourceUsername != null)
+                       // Setup
+                       if (sourceRepository == null)
+                               sourceRepository = ArgeoJcrUtils.getRepositoryByUri(
+                                               repositoryFactory, sourceRepoUri);
+                       if (sourceCredentials == null && sourceUsername != null)
                                sourceCredentials = new SimpleCredentials(sourceUsername,
                                                sourcePassword);
-                       Credentials targetCredentials = null;
-                       if (targetUsername != null)
+                       sourceDefaultSession = sourceRepository.login(sourceCredentials);
+
+                       if (targetRepository == null)
+                               targetRepository = ArgeoJcrUtils.getRepositoryByUri(
+                                               repositoryFactory, targetRepoUri);
+                       if (targetCredentials == null && targetUsername != null)
                                targetCredentials = new SimpleCredentials(targetUsername,
                                                targetPassword);
+                       targetDefaultSession = targetRepository.login(targetCredentials);
+
+                       // Compute job size
+                       if (monitor != null) {
+                               Long totalAmount = 0l;
+                               if (sourceWkspList != null) {
+                                       for (String wkspName : sourceWkspList) {
+                                               totalAmount += getNodesNumber(wkspName);
+                                       }
+                               } else
+                                       for (String sourceWorkspaceName : sourceDefaultSession
+                                                       .getWorkspace().getAccessibleWorkspaceNames()) {
+                                               totalAmount += getNodesNumber(sourceWorkspaceName);
+                                       }
+                               monitor.beginTask("Fetch", totalAmount.intValue());
+                               if (log.isDebugEnabled())
+                                       log.debug("Nb of nodes to sync: " + totalAmount.intValue());
+                       }
 
                        Map<String, Exception> errors = new HashMap<String, Exception>();
-                       sourceDefaultSession = sourceRepository.login(sourceCredentials);
-                       targetDefaultSession = targetRepository.login(targetCredentials);
                        for (String sourceWorkspaceName : sourceDefaultSession
                                        .getWorkspace().getAccessibleWorkspaceNames()) {
-                               if (sourceWksp != null && !sourceWksp.trim().equals("")
-                                               && !sourceWorkspaceName.equals(sourceWksp))
-                                       continue;
-                               if (sourceWorkspaceName.equals("security"))
+
+                               if (sourceWkspList != null
+                                               && !sourceWkspList.contains(sourceWorkspaceName))
                                        continue;
-                               if (sourceWorkspaceName.equals("localrepo"))
+                               if (IGNORED_WSKP_LIST.contains(sourceWorkspaceName))
                                        continue;
+
                                Session sourceSession = null;
                                Session targetSession = null;
                                try {
@@ -114,23 +168,19 @@ public class RepoSync implements Runnable {
                                } catch (Exception e) {
                                        errors.put("Could not sync workspace "
                                                        + sourceWorkspaceName, e);
+                                       if (log.isDebugEnabled())
+                                               e.printStackTrace();
                                } finally {
                                        JcrUtils.logoutQuietly(sourceSession);
                                        JcrUtils.logoutQuietly(targetSession);
                                }
                        }
-                       // Session sourceSession = sourceRepository.login(sourceCredentials,
-                       // sourceWksp);
-                       //
-                       // Credentials targetCredentials = null;
-                       // Session targetSession = targetRepository.login(targetCredentials,
-                       // sourceWksp);
-                       //
-                       // Long count = JcrUtils.copyFiles(sourceSession.getRootNode(),
-                       // targetSession.getRootNode(), true, null);
+
+                       if (monitor != null && monitor.isCanceled())
+                               log.info("Sync has been canceled by user");
 
                        long duration = (System.currentTimeMillis() - begin) / 1000;// s
-                       log.info("Sync " + sourceRepo + " to " + targetRepo + " in "
+                       log.info("Sync " + sourceRepoUri + " to " + targetRepoUri + " in "
                                        + (duration / 60)
 
                                        + "min " + (duration % 60) + "s");
@@ -139,33 +189,49 @@ public class RepoSync implements Runnable {
                                throw new SlcException("Sync failed " + errors);
                        }
                } catch (RepositoryException e) {
-                       throw new SlcException("Cannot sync " + sourceRepo + " to "
-                                       + targetRepo, e);
+                       throw new SlcException("Cannot sync " + sourceRepoUri + " to "
+                                       + targetRepoUri, e);
                } finally {
                        JcrUtils.logoutQuietly(sourceDefaultSession);
                        JcrUtils.logoutQuietly(targetDefaultSession);
                }
        }
 
+       private long getNodesNumber(String wkspName) {
+               if (IGNORED_WSKP_LIST.contains(wkspName))
+                       return 0l;
+               Session sourceSession = null;
+               try {
+                       sourceSession = sourceRepository.login(sourceCredentials, wkspName);
+                       Query countQuery = sourceDefaultSession
+                                       .getWorkspace()
+                                       .getQueryManager()
+                                       .createQuery("select file from [nt:base] as file",
+                                                       Query.JCR_SQL2);
+                       QueryResult result = countQuery.execute();
+                       Long expectedCount = result.getNodes().getSize();
+                       return expectedCount;
+               } catch (RepositoryException e) {
+                       throw new SlcException("Unexpected error while computing "
+                                       + "the size of the fetch for workspace " + wkspName, e);
+               } finally {
+                       JcrUtils.logoutQuietly(sourceSession);
+               }
+       }
+
        protected void syncWorkspace(Session sourceSession, Session targetSession) {
                try {
+                       String msg = "Synchronizing workspace: "
+                                       + sourceSession.getWorkspace().getName();
+                       if (monitor != null)
+                               monitor.setTaskName(msg);
                        if (log.isDebugEnabled())
-                               log.debug("Syncing " + sourceSession.getWorkspace().getName()
-                                               + "...");
+                               log.debug(msg);
                        for (NodeIterator it = sourceSession.getRootNode().getNodes(); it
                                        .hasNext();) {
                                Node node = it.nextNode();
                                if (node.getName().equals("jcr:system"))
                                        continue;
-                               // ContentHandler targetHandler = targetSession
-                               // .getWorkspace()
-                               // .getImportContentHandler(
-                               // "/",
-                               // ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING);
-                               // sourceSession.exportSystemView(node.getPath(), targetHandler,
-                               // true, false);
-                               // if (log.isDebugEnabled())
-                               // log.debug(" " + node.getPath());
                                syncNode(node, targetSession.getRootNode());
                        }
                        if (log.isDebugEnabled())
@@ -177,8 +243,25 @@ public class RepoSync implements Runnable {
                }
        }
 
+       /** factorizes monitor management */
+       private void updateMonitor(String msg) {
+               if (monitor != null) {
+                       monitor.worked(1);
+                       monitor.subTask(msg);
+               }
+       }
+
        protected void syncNode(Node sourceNode, Node targetParentNode)
                        throws RepositoryException, SAXException {
+
+               // enable cancelation of the current fetch process
+               // FIXME insure the repository stays in a stable state
+               if (monitor != null && monitor.isCanceled()) {
+                       updateMonitor("Fetched has been canceled, "
+                                       + "process is terminating");
+                       return;
+               }
+
                Boolean noRecurse = noRecurse(sourceNode);
                Calendar sourceLastModified = null;
                if (sourceNode.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
@@ -187,6 +270,10 @@ public class RepoSync implements Runnable {
                }
 
                if (!targetParentNode.hasNode(sourceNode.getName())) {
+                       String msg = "Adding " + sourceNode.getPath();
+                       updateMonitor(msg);
+                       if (log.isDebugEnabled())
+                               log.debug(msg);
                        ContentHandler contentHandler = targetParentNode
                                        .getSession()
                                        .getWorkspace()
@@ -194,8 +281,6 @@ public class RepoSync implements Runnable {
                                                        ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
                        sourceNode.getSession().exportSystemView(sourceNode.getPath(),
                                        contentHandler, false, noRecurse);
-                       if (log.isDebugEnabled())
-                               log.debug("Added " + sourceNode.getPath());
                } else {
                        Node targetNode = targetParentNode.getNode(sourceNode.getName());
                        if (sourceLastModified != null) {
@@ -207,6 +292,10 @@ public class RepoSync implements Runnable {
 
                                if (targetLastModified == null
                                                || targetLastModified.before(sourceLastModified)) {
+                                       String msg = "Updating " + targetNode.getPath();
+                                       updateMonitor(msg);
+                                       if (log.isDebugEnabled())
+                                               log.debug(msg);
                                        ContentHandler contentHandler = targetParentNode
                                                        .getSession()
                                                        .getWorkspace()
@@ -216,12 +305,11 @@ public class RepoSync implements Runnable {
                                        sourceNode.getSession().exportSystemView(
                                                        sourceNode.getPath(), contentHandler, false,
                                                        noRecurse);
-                                       if (log.isDebugEnabled())
-                                               log.debug("Updated " + targetNode.getPath());
                                } else {
+                                       String msg = "Skipped up to date " + targetNode.getPath();
+                                       updateMonitor(msg);
                                        if (log.isDebugEnabled())
-                                               log.debug("Skipped up to date " + targetNode.getPath());
-                                       // if (!noRecurse)
+                                               log.debug(msg);
                                        return;
                                }
                        }
@@ -247,7 +335,6 @@ public class RepoSync implements Runnable {
                                targetNode.getSession().save();
                        }
                }
-
        }
 
        protected Boolean noRecurse(Node sourceNode) throws RepositoryException {
@@ -256,22 +343,43 @@ public class RepoSync implements Runnable {
                return true;
        }
 
-       public void setSourceRepo(String sourceRepo) {
-               this.sourceRepo = sourceRepo;
+       /** synchronise only one workspace retrieved by name */
+       public void setSourceWksp(String sourceWksp) {
+               if (sourceWksp != null && !sourceWksp.trim().equals("")) {
+                       List<String> list = new ArrayList<String>();
+                       list.add(sourceWksp);
+                       setSourceWkspList(list);
+               }
        }
 
-       public void setTargetRepo(String targetRepo) {
-               this.targetRepo = targetRepo;
+       /** synchronise a list workspace that will be retrieved by name */
+       public void setSourceWkspList(List<String> sourceWkspList) {
+               // clean the list to ease later use
+               this.sourceWkspList = null;
+               if (sourceWkspList != null) {
+                       for (String wkspName : sourceWkspList) {
+                               if (!wkspName.trim().equals("")) {
+                                       // only instantiate if needed
+                                       if (this.sourceWkspList == null)
+                                               this.sourceWkspList = new ArrayList<String>();
+                                       this.sourceWkspList.add(wkspName);
+                               }
+                       }
+               }
        }
 
-       public void setSourceWksp(String sourceWksp) {
-               this.sourceWksp = sourceWksp;
+       public void setMonitor(ArgeoMonitor monitor) {
+               this.monitor = monitor;
        }
 
        public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
                this.repositoryFactory = repositoryFactory;
        }
 
+       public void setSourceRepoUri(String sourceRepoUri) {
+               this.sourceRepoUri = sourceRepoUri;
+       }
+
        public void setSourceUsername(String sourceUsername) {
                this.sourceUsername = sourceUsername;
        }
@@ -280,6 +388,10 @@ public class RepoSync implements Runnable {
                this.sourcePassword = sourcePassword;
        }
 
+       public void setTargetRepoUri(String targetRepoUri) {
+               this.targetRepoUri = targetRepoUri;
+       }
+
        public void setTargetUsername(String targetUsername) {
                this.targetUsername = targetUsername;
        }
@@ -288,4 +400,19 @@ public class RepoSync implements Runnable {
                this.targetPassword = targetPassword;
        }
 
-}
+       public void setSourceRepository(Repository sourceRepository) {
+               this.sourceRepository = sourceRepository;
+       }
+
+       public void setSourceCredentials(Credentials sourceCredentials) {
+               this.sourceCredentials = sourceCredentials;
+       }
+
+       public void setTargetRepository(Repository targetRepository) {
+               this.targetRepository = targetRepository;
+       }
+
+       public void setTargetCredentials(Credentials targetCredentials) {
+               this.targetCredentials = targetCredentials;
+       }
+}
\ No newline at end of file