2 * Copyright (C) 2007-2012 Argeo GmbH
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org
.argeo
.slc
.client
.ui
.dist
.views
;
18 import java
.util
.ArrayList
;
19 import java
.util
.HashMap
;
20 import java
.util
.List
;
23 import javax
.jcr
.Credentials
;
24 import javax
.jcr
.NoSuchWorkspaceException
;
25 import javax
.jcr
.Node
;
26 import javax
.jcr
.NodeIterator
;
27 import javax
.jcr
.Property
;
28 import javax
.jcr
.Repository
;
29 import javax
.jcr
.RepositoryException
;
30 import javax
.jcr
.RepositoryFactory
;
31 import javax
.jcr
.Session
;
32 import javax
.jcr
.nodetype
.NodeType
;
33 import javax
.jcr
.query
.Query
;
34 import javax
.jcr
.query
.QueryResult
;
36 import org
.apache
.commons
.logging
.Log
;
37 import org
.apache
.commons
.logging
.LogFactory
;
38 import org
.argeo
.ArgeoMonitor
;
39 import org
.argeo
.eclipse
.ui
.EclipseArgeoMonitor
;
40 import org
.argeo
.eclipse
.ui
.ErrorFeedback
;
41 import org
.argeo
.eclipse
.ui
.TreeParent
;
42 import org
.argeo
.jcr
.ArgeoJcrUtils
;
43 import org
.argeo
.jcr
.ArgeoNames
;
44 import org
.argeo
.jcr
.ArgeoTypes
;
45 import org
.argeo
.jcr
.JcrUtils
;
46 import org
.argeo
.jcr
.UserJcrUtils
;
47 import org
.argeo
.slc
.SlcException
;
48 import org
.argeo
.slc
.client
.ui
.dist
.DistPlugin
;
49 import org
.argeo
.slc
.client
.ui
.dist
.commands
.CopyWorkspace
;
50 import org
.argeo
.slc
.client
.ui
.dist
.commands
.CreateWorkspace
;
51 import org
.argeo
.slc
.client
.ui
.dist
.commands
.DeleteWorkspace
;
52 import org
.argeo
.slc
.client
.ui
.dist
.commands
.ManageWorkspaceAuth
;
53 import org
.argeo
.slc
.client
.ui
.dist
.commands
.NormalizeDistribution
;
54 import org
.argeo
.slc
.client
.ui
.dist
.editors
.DistributionEditor
;
55 import org
.argeo
.slc
.client
.ui
.dist
.editors
.DistributionEditorInput
;
56 import org
.argeo
.slc
.client
.ui
.dist
.utils
.CommandHelpers
;
57 import org
.argeo
.slc
.jcr
.SlcNames
;
58 import org
.argeo
.slc
.repo
.RepoConstants
;
59 import org
.argeo
.slc
.repo
.RepoUtils
;
60 import org
.argeo
.util
.security
.Keyring
;
61 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
62 import org
.eclipse
.core
.runtime
.IStatus
;
63 import org
.eclipse
.core
.runtime
.Status
;
64 import org
.eclipse
.core
.runtime
.jobs
.Job
;
65 import org
.eclipse
.jface
.action
.IMenuListener
;
66 import org
.eclipse
.jface
.action
.IMenuManager
;
67 import org
.eclipse
.jface
.action
.MenuManager
;
68 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
69 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
70 import org
.eclipse
.jface
.viewers
.DoubleClickEvent
;
71 import org
.eclipse
.jface
.viewers
.IDoubleClickListener
;
72 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
73 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
74 import org
.eclipse
.jface
.viewers
.TreeViewer
;
75 import org
.eclipse
.jface
.viewers
.TreeViewerColumn
;
76 import org
.eclipse
.jface
.viewers
.Viewer
;
77 import org
.eclipse
.jface
.viewers
.ViewerDropAdapter
;
78 import org
.eclipse
.swt
.SWT
;
79 import org
.eclipse
.swt
.dnd
.DND
;
80 import org
.eclipse
.swt
.dnd
.DragSourceAdapter
;
81 import org
.eclipse
.swt
.dnd
.DragSourceEvent
;
82 import org
.eclipse
.swt
.dnd
.TextTransfer
;
83 import org
.eclipse
.swt
.dnd
.Transfer
;
84 import org
.eclipse
.swt
.dnd
.TransferData
;
85 import org
.eclipse
.swt
.widgets
.Composite
;
86 import org
.eclipse
.swt
.widgets
.Menu
;
87 import org
.eclipse
.swt
.widgets
.Tree
;
88 import org
.eclipse
.ui
.IWorkbenchWindow
;
89 import org
.eclipse
.ui
.PartInitException
;
90 import org
.eclipse
.ui
.part
.ViewPart
;
93 * Browse and manipulate distributions (like merge, rename, etc.). Only support
94 * one single repository currently.
96 public class DistributionsView
extends ViewPart
implements SlcNames
, ArgeoNames
{
97 private final static Log log
= LogFactory
.getLog(DistributionsView
.class);
98 public final static String ID
= DistPlugin
.ID
+ ".distributionsView";
100 private Repository nodeRepository
;
101 private RepositoryFactory repositoryFactory
;
102 private Keyring keyring
;
104 private TreeViewer viewer
;
107 public void createPartControl(Composite parent
) {
108 // Define the TableViewer
109 viewer
= new TreeViewer(parent
, SWT
.MULTI
| SWT
.H_SCROLL
| SWT
.V_SCROLL
110 | SWT
.FULL_SELECTION
| SWT
.BORDER
);
112 TreeViewerColumn col
= new TreeViewerColumn(viewer
, SWT
.NONE
);
113 col
.getColumn().setWidth(200);
114 col
.setLabelProvider(new ColumnLabelProvider() {
116 public String
getText(Object element
) {
117 if (element
instanceof RepoElem
)
118 return ((RepoElem
) element
).getLabel();
119 return element
.toString();
123 final Tree table
= viewer
.getTree();
124 table
.setHeaderVisible(false);
125 table
.setLinesVisible(false);
127 viewer
.setContentProvider(new DistributionsContentProvider());
128 viewer
.addDoubleClickListener(new DistributionsDCL());
131 Transfer
[] tt
= new Transfer
[] { TextTransfer
.getInstance() };
132 int operations
= DND
.DROP_COPY
| DND
.DROP_MOVE
;
133 viewer
.addDragSupport(operations
, tt
, new ViewDragListener());
134 viewer
.addDropSupport(operations
, tt
, new ViewDropListener(viewer
));
136 MenuManager menuManager
= new MenuManager();
137 Menu menu
= menuManager
.createContextMenu(viewer
.getTree());
138 menuManager
.addMenuListener(new IMenuListener() {
139 public void menuAboutToShow(IMenuManager manager
) {
140 contextMenuAboutToShow(manager
);
143 viewer
.getTree().setMenu(menu
);
144 getSite().registerContextMenu(menuManager
, viewer
);
146 Session nodeSession
= null;
148 nodeSession
= nodeRepository
.login();
150 // make sure base directory is available
151 Node repos
= JcrUtils
.mkdirs(nodeSession
,
152 UserJcrUtils
.getUserHome(nodeSession
).getPath()
153 + RepoConstants
.REPOSITORIES_BASE_PATH
);
156 // register default local java repository
157 String alias
= RepoConstants
.DEFAULT_JAVA_REPOSITORY_ALIAS
;
158 Repository javaRepository
= ArgeoJcrUtils
.getRepositoryByAlias(
159 repositoryFactory
, alias
);
160 if (javaRepository
!= null) {
161 if (!repos
.hasNode(alias
)) {
162 Node repoNode
= repos
.addNode(alias
,
163 ArgeoTypes
.ARGEO_REMOTE_REPOSITORY
);
164 repoNode
.setProperty(ARGEO_URI
, "vm:///" + alias
);
165 repoNode
.addMixin(NodeType
.MIX_TITLE
);
166 repoNode
.setProperty(Property
.JCR_TITLE
, "Internal "
167 + alias
+ " repository");
171 } catch (RepositoryException e
) {
172 throw new SlcException("Cannot register repository", e
);
174 JcrUtils
.logoutQuietly(nodeSession
);
177 viewer
.setInput(nodeRepository
);
181 /** Programatically configure the context menu */
182 protected void contextMenuAboutToShow(IMenuManager menuManager
) {
183 IWorkbenchWindow window
= DistPlugin
.getDefault().getWorkbench()
184 .getActiveWorkbenchWindow();
185 // Get Current selected item :
186 Object firstElement
= ((IStructuredSelection
) viewer
.getSelection())
189 if (firstElement
instanceof TreeParent
190 || firstElement
instanceof RepoElem
) {
191 String wsName
= null;
193 // Build conditions depending on element type (repo or distribution
194 // for the time being)
195 boolean isDistribElem
= false; // , isRepoElem = false;
197 if (firstElement
instanceof DistributionElem
) {
198 isDistribElem
= true;
199 wsName
= ((DistributionElem
) firstElement
).getName();
203 CommandHelpers
.refreshCommand(menuManager
, window
,
204 CreateWorkspace
.ID
, CreateWorkspace
.DEFAULT_LABEL
,
205 CreateWorkspace
.DEFAULT_ICON_PATH
, !isDistribElem
);
207 // Normalize workspace
208 Map
<String
, String
> params
= new HashMap
<String
, String
>();
209 params
.put(NormalizeDistribution
.PARAM_WORKSPACE
, wsName
);
210 CommandHelpers
.refreshParameterizedCommand(menuManager
, window
,
211 NormalizeDistribution
.ID
,
212 NormalizeDistribution
.DEFAULT_LABEL
,
213 NormalizeDistribution
.DEFAULT_ICON_PATH
, isDistribElem
,
217 params
= new HashMap
<String
, String
>();
218 params
.put(CopyWorkspace
.PARAM_WORKSPACE_NAME
, wsName
);
219 CommandHelpers
.refreshParameterizedCommand(menuManager
, window
,
220 CopyWorkspace
.ID
, CopyWorkspace
.DEFAULT_LABEL
,
221 CopyWorkspace
.DEFAULT_ICON_PATH
, isDistribElem
, params
);
224 params
= new HashMap
<String
, String
>();
225 params
.put(DeleteWorkspace
.PARAM_WORKSPACE_NAME
, wsName
);
226 CommandHelpers
.refreshParameterizedCommand(menuManager
, window
,
227 DeleteWorkspace
.ID
, DeleteWorkspace
.DEFAULT_LABEL
,
228 DeleteWorkspace
.DEFAULT_ICON_PATH
, isDistribElem
, params
);
230 // Manage workspace authorizations
231 params
= new HashMap
<String
, String
>();
232 params
.put(ManageWorkspaceAuth
.PARAM_WORKSPACE_NAME
, wsName
);
233 CommandHelpers
.refreshParameterizedCommand(menuManager
, window
,
234 ManageWorkspaceAuth
.ID
, ManageWorkspaceAuth
.DEFAULT_LABEL
,
235 ManageWorkspaceAuth
.DEFAULT_ICON_PATH
, isDistribElem
,
241 public void setFocus() {
242 viewer
.getTree().setFocus();
246 * DEPENDENCY INJECTION
249 * Force refresh of the whole view
251 public void refresh() {
252 viewer
.setContentProvider(new DistributionsContentProvider());
255 public void setNodeRepository(Repository repository
) {
256 this.nodeRepository
= repository
;
259 public void setRepositoryFactory(RepositoryFactory repositoryFactory
) {
260 this.repositoryFactory
= repositoryFactory
;
263 public void setKeyring(Keyring keyring
) {
264 this.keyring
= keyring
;
270 /** Content provider */
271 private class DistributionsContentProvider
implements ITreeContentProvider
{
273 List
<RepoElem
> repositories
= new ArrayList
<RepoElem
>();
275 public Object
[] getElements(Object input
) {
276 Repository nodeRepository
= (Repository
) input
;
278 if (nodeSession
!= null)
280 nodeSession
= nodeRepository
.login();
282 String reposPath
= UserJcrUtils
.getUserHome(nodeSession
)
283 .getPath() + RepoConstants
.REPOSITORIES_BASE_PATH
;
284 NodeIterator repos
= nodeSession
.getNode(reposPath
).getNodes();
285 while (repos
.hasNext()) {
286 Node repoNode
= repos
.nextNode();
287 if (repoNode
.isNodeType(ArgeoTypes
.ARGEO_REMOTE_REPOSITORY
)) {
288 repositories
.add(new RepoElem(repoNode
));
291 } catch (RepositoryException e
) {
292 throw new SlcException("Cannot get base elements", e
);
294 return repositories
.toArray();
297 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
301 public Object
[] getChildren(Object parentElement
) {
302 if (parentElement
instanceof RepoElem
) {
303 return ((RepoElem
) parentElement
).getChildren();
304 } else if (parentElement
instanceof DistributionElem
) {
305 return ((DistributionElem
) parentElement
).getChildren();
310 public Object
getParent(Object element
) {
311 // TODO register repo elem in distirbution elem?
315 public boolean hasChildren(Object element
) {
316 if (element
instanceof RepoElem
) {
318 } else if (element
instanceof DistributionElem
) {
324 public void dispose() {
325 for (RepoElem repoElem
: repositories
)
327 repositories
= new ArrayList
<RepoElem
>();
328 JcrUtils
.logoutQuietly(nodeSession
);
333 /** A software repository */
334 private class RepoElem
{
335 private Node repoNode
;
337 private Repository repository
;
338 private Credentials credentials
;
340 public RepoElem(Node repoNode
) {
341 this.repoNode
= repoNode
;
344 /** Lazily connects to repository */
345 protected void connect() {
346 if (repository
!= null)
348 repository
= RepoUtils
.getRepository(repositoryFactory
, keyring
,
350 credentials
= RepoUtils
.getRepositoryCredentials(keyring
, repoNode
);
353 public String
getLabel() {
355 if (repoNode
.isNodeType(NodeType
.MIX_TITLE
)) {
356 return repoNode
.getProperty(Property
.JCR_TITLE
).getString();
358 return repoNode
.getName();
360 } catch (RepositoryException e
) {
361 throw new SlcException("Cannot read label of " + repoNode
, e
);
365 public String
toString() {
366 return repoNode
.toString();
369 public Object
[] getChildren() {
371 Session session
= null;
373 session
= repository
.login(credentials
);
374 String
[] workspaceNames
= session
.getWorkspace()
375 .getAccessibleWorkspaceNames();
376 List
<DistributionElem
> distributionElems
= new ArrayList
<DistributionElem
>();
377 for (String workspaceName
: workspaceNames
) {
378 Node workspaceNode
= repoNode
.hasNode(workspaceName
) ? repoNode
379 .getNode(workspaceName
) : repoNode
380 .addNode(workspaceName
);
381 repoNode
.getSession().save();
382 distributionElems
.add(new DistributionElem(this,
384 // FIXME remove deleted workspaces
386 return distributionElems
.toArray();
387 } catch (RepositoryException e
) {
388 throw new SlcException(
389 "Cannot list workspaces for " + repoNode
, e
);
391 JcrUtils
.logoutQuietly(session
);
395 public void dispose() {
398 public Node
getRepoNode() {
402 public String
getRepoPath() {
404 return repoNode
.getPath();
405 } catch (RepositoryException e
) {
406 throw new SlcException("Cannot get path for " + repoNode
, e
);
410 public Repository
getRepository() {
415 public Credentials
getCredentials() {
421 /** Abstracts a distribution, that is a workspace */
422 private static class DistributionElem
extends TreeParent
{
423 private final RepoElem repoElem
;
424 private final Node workspaceNode
;
426 public DistributionElem(RepoElem repoElem
, Node workspaceNode
) {
427 super(JcrUtils
.getNameQuietly(workspaceNode
));
428 this.repoElem
= repoElem
;
429 this.workspaceNode
= workspaceNode
;
432 public Node
getWorkspaceNode() {
433 return workspaceNode
;
436 public String
getWorkspaceName() {
437 return JcrUtils
.getNameQuietly(workspaceNode
);
440 public String
getWorkspacePath() {
442 return workspaceNode
.getPath();
443 } catch (RepositoryException e
) {
444 throw new SlcException("Cannot get or add workspace path "
445 + getWorkspaceName(), e
);
449 public String
getRepoPath() {
451 return workspaceNode
.getParent().getPath();
452 } catch (RepositoryException e
) {
453 throw new SlcException("Cannot get or add workspace path "
454 + getWorkspaceName(), e
);
458 public RepoElem
getRepoElem() {
462 public Credentials
getCredentials() {
463 return repoElem
.getCredentials();
467 /** Listens to drag */
468 class ViewDragListener
extends DragSourceAdapter
{
469 public void dragSetData(DragSourceEvent event
) {
470 IStructuredSelection selection
= (IStructuredSelection
) viewer
472 if (selection
.getFirstElement() instanceof DistributionElem
) {
473 DistributionElem de
= (DistributionElem
) selection
475 if (TextTransfer
.getInstance().isSupportedType(event
.dataType
)) {
476 event
.data
= de
.getWorkspacePath();
482 /** Listens to drop */
483 class ViewDropListener
extends ViewerDropAdapter
{
485 public ViewDropListener(Viewer viewer
) {
490 public boolean performDrop(Object data
) {
491 DistributionElem sourceDist
= (DistributionElem
) getSelectedObject();
492 RepoElem targetRepo
= (RepoElem
) getCurrentTarget();
494 Boolean ok
= MessageDialog
.openConfirm(getSite().getShell(),
495 "Confirm distribution merge", "Do you want to merge "
496 + sourceDist
.getWorkspaceName() + " (from repo "
497 + sourceDist
.getRepoElem().getLabel()
498 + ") to repo " + targetRepo
.getLabel() + "?");
503 String sourceWorkspace
= sourceDist
.getWorkspaceName();
504 Repository sourceRepository
= RepoUtils
.getRepository(
505 repositoryFactory
, keyring
, sourceDist
506 .getWorkspaceNode().getParent());
507 Credentials sourceCredentials
= RepoUtils
508 .getRepositoryCredentials(keyring
, sourceDist
509 .getWorkspaceNode().getParent());
511 String targetWorkspace
= sourceWorkspace
;
512 Repository targetRepository
= RepoUtils
.getRepository(
513 repositoryFactory
, keyring
, targetRepo
.getRepoNode());
514 Credentials targetCredentials
= RepoUtils
515 .getRepositoryCredentials(keyring
,
516 targetRepo
.getRepoNode());
518 // Open sessions here since the background thread
519 // won't necessarily be authenticated.
520 // Job should close the sessions.
521 Session sourceSession
= sourceRepository
.login(
522 sourceCredentials
, sourceWorkspace
);
523 Session targetSession
;
525 targetSession
= targetRepository
.login(targetCredentials
,
527 } catch (NoSuchWorkspaceException e
) {
528 Session defaultSession
= targetRepository
529 .login(targetCredentials
);
531 defaultSession
.getWorkspace().createWorkspace(
533 } catch (Exception e1
) {
534 throw new SlcException("Cannot create new workspace "
535 + targetWorkspace
, e
);
537 JcrUtils
.logoutQuietly(defaultSession
);
539 targetSession
= targetRepository
.login(targetCredentials
,
543 Job workspaceMergeJob
= new WorkspaceMergeJob(sourceSession
,
545 workspaceMergeJob
.setUser(true);
546 workspaceMergeJob
.schedule();
548 } catch (RepositoryException e
) {
549 throw new SlcException("Cannot process drop from " + sourceDist
550 + " to " + targetRepo
, e
);
555 public boolean validateDrop(Object target
, int operation
,
556 TransferData transferType
) {
557 if (target
instanceof RepoElem
) {
558 if (getSelectedObject() instanceof DistributionElem
) {
559 // check if not same repository
560 String srcRepoPath
= ((DistributionElem
) getSelectedObject())
562 String targetRepoPath
= ((RepoElem
) target
).getRepoPath();
563 return !targetRepoPath
.equals(srcRepoPath
);
570 private static class WorkspaceMergeJob
extends Job
{
571 private Session sourceSession
;
572 private Session targetSession
;
574 public WorkspaceMergeJob(Session sourceSession
, Session targetSession
) {
575 super("Workspace merge");
576 this.sourceSession
= sourceSession
;
577 this.targetSession
= targetSession
;
581 protected IStatus
run(IProgressMonitor eclipseMonitor
) {
582 long begin
= System
.currentTimeMillis();
584 Query countQuery
= sourceSession
587 .createQuery("select file from [nt:file] as file",
589 QueryResult result
= countQuery
.execute();
590 Long expectedCount
= result
.getNodes().getSize();
591 if (log
.isDebugEnabled())
592 log
.debug("Will copy " + expectedCount
+ " files...");
594 ArgeoMonitor monitor
= new EclipseArgeoMonitor(eclipseMonitor
);
596 .beginTask("Copy files", expectedCount
.intValue());
598 Long count
= JcrUtils
.copyFiles(sourceSession
.getRootNode(),
599 targetSession
.getRootNode(), true, monitor
);
602 long duration
= (System
.currentTimeMillis() - begin
) / 1000;// in
604 if (log
.isDebugEnabled())
605 log
.debug("Copied " + count
+ " files in "
606 + (duration
/ 60) + "min " + (duration
% 60) + "s");
608 return Status
.OK_STATUS
;
609 } catch (RepositoryException e
) {
610 return new Status(IStatus
.ERROR
, DistPlugin
.ID
, "Cannot merge",
613 JcrUtils
.logoutQuietly(sourceSession
);
614 JcrUtils
.logoutQuietly(targetSession
);
619 /** Listen to double-clicks */
620 private class DistributionsDCL
implements IDoubleClickListener
{
622 public void doubleClick(DoubleClickEvent event
) {
623 if (event
.getSelection() == null || event
.getSelection().isEmpty())
625 Object obj
= ((IStructuredSelection
) event
.getSelection())
627 if (obj
instanceof DistributionElem
) {
628 DistributionElem distributionElem
= (DistributionElem
) obj
;
629 DistributionEditorInput dei
= new DistributionEditorInput(
630 distributionElem
.getName(), distributionElem
631 .getRepoElem().getRepository(),
632 distributionElem
.getWorkspaceName(),
633 distributionElem
.getCredentials());
635 DistPlugin
.getDefault().getWorkbench()
636 .getActiveWorkbenchWindow().getActivePage()
637 .openEditor(dei
, DistributionEditor
.ID
);
638 } catch (PartInitException e
) {
639 ErrorFeedback
.show("Cannot open editor for "
640 + distributionElem
.getWorkspaceName(), e
);