]> git.argeo.org Git - gpl/argeo-slc.git/blob - plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/views/DistributionsView.java
+ update Distribution view icons
[gpl/argeo-slc.git] / plugins / org.argeo.slc.client.ui.dist / src / main / java / org / argeo / slc / client / ui / dist / views / DistributionsView.java
1 /*
2 * Copyright (C) 2007-2012 Argeo GmbH
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16 package org.argeo.slc.client.ui.dist.views;
17
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22
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;
35
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.DistImages;
49 import org.argeo.slc.client.ui.dist.DistPlugin;
50 import org.argeo.slc.client.ui.dist.commands.CopyWorkspace;
51 import org.argeo.slc.client.ui.dist.commands.CreateWorkspace;
52 import org.argeo.slc.client.ui.dist.commands.DeleteWorkspace;
53 import org.argeo.slc.client.ui.dist.commands.DisplayRepoInformation;
54 import org.argeo.slc.client.ui.dist.commands.Fetch;
55 import org.argeo.slc.client.ui.dist.commands.NormalizeDistribution;
56 import org.argeo.slc.client.ui.dist.commands.PublishWorkspace;
57 import org.argeo.slc.client.ui.dist.commands.RegisterRepository;
58 import org.argeo.slc.client.ui.dist.commands.UnregisterRemoteRepo;
59 import org.argeo.slc.client.ui.dist.editors.DistributionEditor;
60 import org.argeo.slc.client.ui.dist.editors.DistributionEditorInput;
61 import org.argeo.slc.client.ui.dist.utils.ArtifactNamesComparator;
62 import org.argeo.slc.client.ui.dist.utils.CommandHelpers;
63 import org.argeo.slc.jcr.SlcNames;
64 import org.argeo.slc.repo.RepoConstants;
65 import org.argeo.slc.repo.RepoUtils;
66 import org.argeo.util.security.Keyring;
67 import org.eclipse.core.runtime.IProgressMonitor;
68 import org.eclipse.core.runtime.IStatus;
69 import org.eclipse.core.runtime.Status;
70 import org.eclipse.core.runtime.jobs.Job;
71 import org.eclipse.jface.action.IMenuListener;
72 import org.eclipse.jface.action.IMenuManager;
73 import org.eclipse.jface.action.MenuManager;
74 import org.eclipse.jface.dialogs.MessageDialog;
75 import org.eclipse.jface.viewers.ColumnLabelProvider;
76 import org.eclipse.jface.viewers.DoubleClickEvent;
77 import org.eclipse.jface.viewers.IDoubleClickListener;
78 import org.eclipse.jface.viewers.IStructuredSelection;
79 import org.eclipse.jface.viewers.ITreeContentProvider;
80 import org.eclipse.jface.viewers.TreeViewer;
81 import org.eclipse.jface.viewers.TreeViewerColumn;
82 import org.eclipse.jface.viewers.Viewer;
83 import org.eclipse.jface.viewers.ViewerDropAdapter;
84 import org.eclipse.swt.SWT;
85 import org.eclipse.swt.dnd.DND;
86 import org.eclipse.swt.dnd.DragSourceAdapter;
87 import org.eclipse.swt.dnd.DragSourceEvent;
88 import org.eclipse.swt.dnd.TextTransfer;
89 import org.eclipse.swt.dnd.Transfer;
90 import org.eclipse.swt.dnd.TransferData;
91 import org.eclipse.swt.graphics.Image;
92 import org.eclipse.swt.widgets.Composite;
93 import org.eclipse.swt.widgets.Menu;
94 import org.eclipse.swt.widgets.Tree;
95 import org.eclipse.ui.IWorkbenchWindow;
96 import org.eclipse.ui.PartInitException;
97 import org.eclipse.ui.part.ViewPart;
98
99 /**
100 * Browse, manipulate and manage distributions accross multiple repositories
101 * (like fetch, merge, publish, etc.).
102 */
103 public class DistributionsView extends ViewPart implements SlcNames, ArgeoNames {
104 private final static Log log = LogFactory.getLog(DistributionsView.class);
105 public final static String ID = DistPlugin.ID + ".distributionsView";
106
107 private Repository nodeRepository;
108 private RepositoryFactory repositoryFactory;
109 private Keyring keyring;
110
111 private TreeViewer viewer;
112
113 @Override
114 public void createPartControl(Composite parent) {
115 // Define the TableViewer
116 viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL
117 | SWT.FULL_SELECTION | SWT.BORDER);
118
119 TreeViewerColumn col = new TreeViewerColumn(viewer, SWT.NONE);
120 col.getColumn().setWidth(400);
121 col.setLabelProvider(new DistributionLabelProvider());
122
123 final Tree tree = viewer.getTree();
124 tree.setHeaderVisible(false);
125 tree.setLinesVisible(false);
126
127 viewer.setContentProvider(new DistributionsContentProvider());
128 viewer.addDoubleClickListener(new DistributionsDCL());
129 viewer.setComparator(new BrowserElementComparator());
130
131 // Enable selection retrieving from outside the view
132 getSite().setSelectionProvider(viewer);
133
134 // Drag'n drop
135 Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
136 int operations = DND.DROP_COPY | DND.DROP_MOVE;
137 viewer.addDragSupport(operations, tt, new ViewDragListener());
138 viewer.addDropSupport(operations, tt, new ViewDropListener(viewer));
139
140 MenuManager menuManager = new MenuManager();
141 Menu menu = menuManager.createContextMenu(viewer.getTree());
142 menuManager.addMenuListener(new IMenuListener() {
143 public void menuAboutToShow(IMenuManager manager) {
144 contextMenuAboutToShow(manager);
145 }
146 });
147 viewer.getTree().setMenu(menu);
148 getSite().registerContextMenu(menuManager, viewer);
149
150 Session nodeSession = null;
151 try {
152 nodeSession = nodeRepository.login();
153
154 // make sure base directory is available
155 Node repos = JcrUtils.mkdirs(nodeSession,
156 UserJcrUtils.getUserHome(nodeSession).getPath()
157 + RepoConstants.REPOSITORIES_BASE_PATH);
158 nodeSession.save();
159
160 // register default local java repository
161 String alias = RepoConstants.DEFAULT_JAVA_REPOSITORY_ALIAS;
162 Repository javaRepository = ArgeoJcrUtils.getRepositoryByAlias(
163 repositoryFactory, alias);
164 if (javaRepository != null) {
165 if (!repos.hasNode(alias)) {
166 Node repoNode = repos.addNode(alias,
167 ArgeoTypes.ARGEO_REMOTE_REPOSITORY);
168 repoNode.setProperty(ARGEO_URI, "vm:///" + alias);
169 repoNode.addMixin(NodeType.MIX_TITLE);
170 repoNode.setProperty(Property.JCR_TITLE,
171 RepoConstants.DEFAULT_JAVA_REPOSITORY_LABEL);
172 nodeSession.save();
173 }
174 }
175 } catch (RepositoryException e) {
176 throw new SlcException("Cannot register repository", e);
177 } finally {
178 JcrUtils.logoutQuietly(nodeSession);
179 }
180
181 viewer.setInput(nodeRepository);
182 viewer.expandToLevel(2);
183 }
184
185 /** Programatically configure the context menu */
186 protected void contextMenuAboutToShow(IMenuManager menuManager) {
187 IWorkbenchWindow window = DistPlugin.getDefault().getWorkbench()
188 .getActiveWorkbenchWindow();
189 // Most of the implemented commands support only one selected
190 // element
191 boolean singleElement = ((IStructuredSelection) viewer.getSelection())
192 .size() == 1;
193 // Get Current selected item :
194 Object firstElement = ((IStructuredSelection) viewer.getSelection())
195 .getFirstElement();
196
197 if (firstElement instanceof TreeParent
198 || firstElement instanceof BrowserElem) {
199 String targetRepoPath = null;
200
201 // Build conditions depending on element type
202 boolean isDistribElem = false, isRepoElem = false, isDistribGroupElem = false;
203 boolean isHomeRepo = false, isReadOnly = true;
204
205 if (firstElement instanceof DistributionElem) {
206 DistributionElem de = (DistributionElem) firstElement;
207 isDistribElem = true;
208 isReadOnly = de.isReadOnly();
209 } else if (firstElement instanceof RepoElem) {
210 RepoElem re = (RepoElem) firstElement;
211 isRepoElem = true;
212 targetRepoPath = re.getRepoPath();
213 isHomeRepo = re.isHomeRepo();
214 isReadOnly = re.isReadOnly();
215 } else if (firstElement instanceof DistribGroupElem) {
216 DistribGroupElem dge = (DistribGroupElem) firstElement;
217 isReadOnly = dge.isReadOnly();
218 isDistribGroupElem = true;
219 }
220
221 // Display repo info
222 CommandHelpers.refreshCommand(menuManager, window,
223 DisplayRepoInformation.ID,
224 DisplayRepoInformation.DEFAULT_LABEL,
225 DisplayRepoInformation.DEFAULT_ICON_PATH, isRepoElem
226 && singleElement);
227
228 // create workspace
229 CommandHelpers.refreshCommand(menuManager, window,
230 CreateWorkspace.ID, CreateWorkspace.DEFAULT_LABEL,
231 CreateWorkspace.DEFAULT_ICON_PATH,
232 (isRepoElem || isDistribGroupElem) && singleElement
233 && !isReadOnly);
234 // publish workspace
235 CommandHelpers.refreshCommand(menuManager, window,
236 PublishWorkspace.ID, PublishWorkspace.DEFAULT_LABEL,
237 PublishWorkspace.DEFAULT_ICON_PATH, isDistribElem
238 && singleElement && !isReadOnly);
239
240 // Register a remote repository
241 CommandHelpers.refreshCommand(menuManager, window,
242 RegisterRepository.ID, RegisterRepository.DEFAULT_LABEL,
243 RegisterRepository.DEFAULT_ICON_PATH, isRepoElem
244 && singleElement);
245
246 // Unregister a remote repository
247 Map<String, String> params = new HashMap<String, String>();
248 params.put(UnregisterRemoteRepo.PARAM_REPO_PATH, targetRepoPath);
249 CommandHelpers.refreshParameterizedCommand(menuManager, window,
250 UnregisterRemoteRepo.ID,
251 UnregisterRemoteRepo.DEFAULT_LABEL,
252 UnregisterRemoteRepo.DEFAULT_ICON_PATH, isRepoElem
253 && !isHomeRepo && singleElement, params);
254
255 // Fetch repository
256 params = new HashMap<String, String>();
257 params.put(Fetch.PARAM_TARGET_REPO, targetRepoPath);
258 CommandHelpers.refreshParameterizedCommand(menuManager, window,
259 Fetch.ID, Fetch.DEFAULT_LABEL, Fetch.DEFAULT_ICON_PATH,
260 isRepoElem && singleElement && !isReadOnly, params);
261
262 // Normalize workspace
263 CommandHelpers.refreshCommand(menuManager, window,
264 NormalizeDistribution.ID,
265 NormalizeDistribution.DEFAULT_LABEL,
266 NormalizeDistribution.DEFAULT_ICON_PATH, isDistribElem
267 && singleElement && !isReadOnly);
268
269 // Copy workspace
270 CommandHelpers.refreshCommand(menuManager, window,
271 CopyWorkspace.ID, CopyWorkspace.DEFAULT_LABEL,
272 CopyWorkspace.DEFAULT_ICON_PATH, isDistribElem
273 && singleElement);
274
275 // Clear Workspace
276 CommandHelpers.refreshCommand(menuManager, window,
277 DeleteWorkspace.ID, DeleteWorkspace.DEFAULT_LABEL,
278 DeleteWorkspace.DEFAULT_ICON_PATH, isDistribElem
279 && singleElement && !isReadOnly);
280
281 // // Manage workspace authorizations
282 // params = new HashMap<String, String>();
283 // params.put(ManageWorkspaceAuth.PARAM_WORKSPACE_NAME, wsName);
284 // CommandHelpers.refreshParameterizedCommand(menuManager, window,
285 // ManageWorkspaceAuth.ID, ManageWorkspaceAuth.DEFAULT_LABEL,
286 // ManageWorkspaceAuth.DEFAULT_ICON_PATH, isDistribElem
287 // && singleElement && !isReadOnly, params);
288 }
289 // } catch (RepositoryException e) {
290 // throw new SlcException("unexpected errror while "
291 // + "building context menu", e);
292 // }
293 }
294
295 /**
296 * Exposes some Repository and workspace information about the selected
297 * element without exposing the UI model
298 */
299 public class DistributionViewSelectedElement {
300 public boolean isRepository = false;
301 public boolean isWorkspaceGroup = false;
302 public boolean isWorkspace = false;
303 public boolean isReadOnly = false;
304 public String repositoryDescription;
305 public Node repoNode;
306 public String wkspName;
307 public String wkspPrefix;
308 public Repository repository;
309 public Credentials credentials;
310 }
311
312 /**
313 * Returns a {@see DistributionViewSelectedElement} if one and only one
314 * valid element is currently selected.
315 *
316 */
317 public DistributionViewSelectedElement getSelectedElement() {
318
319 IStructuredSelection iss = (IStructuredSelection) viewer.getSelection();
320 if (iss.isEmpty() || iss.size() > 1)
321 return null;
322
323 DistributionViewSelectedElement dvse = new DistributionViewSelectedElement();
324 Object obj = iss.getFirstElement();
325 if (obj instanceof RepoElem) {
326 RepoElem re = (RepoElem) obj;
327 dvse.isRepository = true;
328 dvse.isReadOnly = re.isReadOnly();
329 dvse.repository = re.getRepository();
330 dvse.repoNode = re.getRepoNode();
331 dvse.credentials = re.getCredentials();
332 dvse.repositoryDescription = getRepositoryDescription(re);
333 } else if (obj instanceof DistribGroupElem) {
334 DistribGroupElem dge = (DistribGroupElem) obj;
335 dvse.isWorkspaceGroup = true;
336 dvse.isReadOnly = dge.isReadOnly();
337 dvse.repository = dge.getRepoElem().getRepository();
338 dvse.repoNode = dge.getRepoElem().getRepoNode();
339 dvse.credentials = dge.getRepoElem().getCredentials();
340 dvse.wkspPrefix = dge.getLabel();
341 dvse.repositoryDescription = getRepositoryDescription(dge
342 .getRepoElem());
343 } else if (obj instanceof DistributionElem) {
344 DistributionElem de = (DistributionElem) obj;
345 dvse.isWorkspace = true;
346 dvse.isReadOnly = de.isReadOnly();
347 dvse.repository = de.getRepoElem().getRepository();
348 dvse.repoNode = de.getRepoElem().getRepoNode();
349 dvse.credentials = de.getRepoElem().getCredentials();
350 dvse.wkspName = de.getWorkspaceName();
351 dvse.repositoryDescription = getRepositoryDescription(de
352 .getRepoElem());
353 }
354 return dvse;
355 }
356
357 private String getRepositoryDescription(RepoElem repo) {
358 StringBuffer repoDesc = new StringBuffer();
359 repoDesc.append(repo.getLabel());
360 repoDesc.append(" (");
361 repoDesc.append(JcrUtils.get(repo.getRepoNode(), ARGEO_URI));
362 repoDesc.append(")");
363 return repoDesc.toString();
364 }
365
366 @Override
367 public void setFocus() {
368 viewer.getTree().setFocus();
369 }
370
371 /**
372 * Force refresh of the whole view
373 */
374 public void refresh() {
375 viewer.setContentProvider(new DistributionsContentProvider());
376 }
377
378 /*
379 * UI MODEL
380 */
381 private class DistributionLabelProvider extends ColumnLabelProvider {
382 @Override
383 public String getText(Object element) {
384 if (element instanceof BrowserElem)
385 return ((BrowserElem) element).getLabel();
386 else
387 return element.toString();
388 }
389
390 @Override
391 public Image getImage(Object element) {
392 if (element instanceof BrowserElem) {
393 BrowserElem bElement = (BrowserElem) element;
394 if (bElement instanceof RepoElem) {
395 if (bElement.isHomeRepo())
396 return DistImages.IMG_HOME_REPO;
397 else if (bElement.isReadOnly())
398 return DistImages.IMG_REPO_READONLY;
399 else
400 return DistImages.IMG_REPO;
401
402 } else if (bElement instanceof DistribGroupElem) {
403 return DistImages.IMG_WKSP;
404 }
405 } else if (element instanceof DistributionElem)
406 if (((DistributionElem) element).isReadOnly())
407 return DistImages.IMG_DISTGRP_READONLY;
408 else
409 return DistImages.IMG_DISTGRP;
410
411 return null;
412 }
413 }
414
415 /** Content provider */
416 private class DistributionsContentProvider implements ITreeContentProvider {
417 Session nodeSession;
418 List<RepoElem> repositories = new ArrayList<RepoElem>();
419
420 public Object[] getElements(Object input) {
421 Repository nodeRepository = (Repository) input;
422 try {
423 if (nodeSession != null)
424 dispose();
425 nodeSession = nodeRepository.login();
426
427 String reposPath = UserJcrUtils.getUserHome(nodeSession)
428 .getPath() + RepoConstants.REPOSITORIES_BASE_PATH;
429 NodeIterator repos = nodeSession.getNode(reposPath).getNodes();
430 while (repos.hasNext()) {
431 Node repoNode = repos.nextNode();
432 if (repoNode.isNodeType(ArgeoTypes.ARGEO_REMOTE_REPOSITORY)) {
433 if (RepoConstants.DEFAULT_JAVA_REPOSITORY_ALIAS
434 .equals(repoNode.getName()))
435 repositories
436 .add(new RepoElem(repoNode, true, false));
437 else if (repoNode.hasNode(ARGEO_PASSWORD))
438 repositories.add(new RepoElem(repoNode));
439 else
440 repositories
441 .add(new RepoElem(repoNode, false, true));
442 }
443 }
444 } catch (RepositoryException e) {
445 throw new SlcException("Cannot get base elements", e);
446 }
447 return repositories.toArray();
448 }
449
450 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
451 }
452
453 public Object[] getChildren(Object parentElement) {
454 if (parentElement instanceof BrowserElem) {
455 return ((BrowserElem) parentElement).getChildren();
456 } else if (parentElement instanceof DistributionElem) {
457 return ((DistributionElem) parentElement).getChildren();
458 }
459 return null;
460 }
461
462 public Object getParent(Object element) {
463 // TODO register repo elem in distribution elem?
464 return null;
465 }
466
467 public boolean hasChildren(Object element) {
468 if (element instanceof BrowserElem) {
469 return true;
470 } else if (element instanceof DistributionElem) {
471 return false;
472 }
473 return false;
474 }
475
476 public void dispose() {
477 for (RepoElem repoElem : repositories)
478 repoElem.dispose();
479 repositories = new ArrayList<RepoElem>();
480 JcrUtils.logoutQuietly(nodeSession);
481 }
482 }
483
484 /** Add some view specific behaviours to the comparator */
485 private class BrowserElementComparator extends ArtifactNamesComparator {
486 @Override
487 public int category(Object element) {
488 // Home repository always first
489 if (element instanceof RepoElem
490 && ((RepoElem) element).isHomeRepo())
491 return 2;
492 else
493 return super.category(element);
494 }
495
496 @Override
497 public int compare(Viewer viewer, Object e1, Object e2) {
498 // reverse order for versions
499 if (e1 instanceof DistributionElem)
500 return -super.compare(viewer, e1, e2);
501 else
502 return super.compare(viewer, e1, e2);
503 }
504 }
505
506 /** Abstract class to simplify UI conditions build */
507 abstract class BrowserElem {
508 private boolean isHomeRepo = false;
509 private boolean isReadOnly = false;
510
511 public BrowserElem(boolean isHomeRepo, boolean isReadOnly) {
512 this.isHomeRepo = isHomeRepo;
513 this.isReadOnly = isReadOnly;
514 }
515
516 public BrowserElem() {
517 }
518
519 public abstract String getLabel();
520
521 public abstract Object[] getChildren();
522
523 public void dispose() {
524 }
525
526 public boolean isHomeRepo() {
527 return isHomeRepo;
528 }
529
530 public boolean isReadOnly() {
531 return isReadOnly;
532 }
533 }
534
535 /** A software repository */
536 private class RepoElem extends BrowserElem {
537 private Node repoNode;
538 private Repository repository;
539 private Credentials credentials;
540
541 public RepoElem(Node repoNode, boolean isHomeRepo, boolean isReadOnly) {
542 super(isHomeRepo, isReadOnly);
543 this.repoNode = repoNode;
544 }
545
546 public RepoElem(Node repoNode) {
547 this.repoNode = repoNode;
548 }
549
550 /** Lazily connects to repository */
551 protected void connect() {
552 if (repository != null)
553 return;
554 repository = RepoUtils.getRepository(repositoryFactory, keyring,
555 repoNode);
556 credentials = RepoUtils.getRepositoryCredentials(keyring, repoNode);
557 }
558
559 public String getLabel() {
560 try {
561 if (repoNode.isNodeType(NodeType.MIX_TITLE)) {
562 return repoNode.getProperty(Property.JCR_TITLE).getString();
563 } else {
564 return repoNode.getName();
565 }
566 } catch (RepositoryException e) {
567 throw new SlcException("Cannot read label of " + repoNode, e);
568 }
569 }
570
571 public String toString() {
572 return repoNode.toString();
573 }
574
575 public Object[] getChildren() {
576 connect();
577 Session session = null;
578 try {
579 session = repository.login(credentials);
580 String[] workspaceNames = session.getWorkspace()
581 .getAccessibleWorkspaceNames();
582 // List<DistributionElem> distributionElems = new
583 // ArrayList<DistributionElem>();
584 Map<String, DistribGroupElem> children = new HashMap<String, DistributionsView.DistribGroupElem>();
585 for (String workspaceName : workspaceNames) {
586 // filter technical workspaces
587 // FIXME: rely on a more robust rule than just wksp name
588 if (workspaceName.lastIndexOf('-') > 0) {
589 String prefix = workspaceName.substring(0,
590 workspaceName.lastIndexOf('-'));
591 if (!repoNode.hasNode(workspaceName))
592 repoNode.addNode(workspaceName);
593 repoNode.getSession().save();
594 if (!children.containsKey(prefix)) {
595 children.put(prefix, new DistribGroupElem(
596 RepoElem.this, prefix));
597 }
598 // FIXME remove deleted workspaces
599 }
600 }
601 return children.values().toArray();
602 } catch (RepositoryException e) {
603 throw new SlcException(
604 "Cannot list workspaces for " + repoNode, e);
605 } finally {
606 JcrUtils.logoutQuietly(session);
607 }
608 }
609
610 public String getRepoPath() {
611 try {
612 return repoNode.getPath();
613 } catch (RepositoryException e) {
614 throw new SlcException("Cannot get path for " + repoNode, e);
615 }
616 }
617
618 public Repository getRepository() {
619 connect();
620 return repository;
621 }
622
623 public Credentials getCredentials() {
624 return credentials;
625 }
626
627 public Node getRepoNode() {
628 return repoNode;
629 }
630
631 }
632
633 /**
634 * Abstracts a group of distribution, that is a bunch of workspaces with
635 * same prefix.
636 */
637 private class DistribGroupElem extends BrowserElem {
638 private RepoElem repoElem;
639 private String name;
640
641 public DistribGroupElem(RepoElem repoElem, String prefix) {
642 super(repoElem.isHomeRepo(), repoElem.isReadOnly());
643 this.repoElem = repoElem;
644 this.name = prefix;
645 }
646
647 public Object[] getChildren() {
648 repoElem.connect();
649 Session session = null;
650 try {
651 Repository repository = repoElem.getRepository();
652 Node repoNode = repoElem.getRepoNode();
653 session = repository.login(repoElem.getCredentials());
654
655 String[] workspaceNames = session.getWorkspace()
656 .getAccessibleWorkspaceNames();
657 List<DistributionElem> distributionElems = new ArrayList<DistributionElem>();
658 for (String workspaceName : workspaceNames) {
659 // filter technical workspaces
660 if (workspaceName.startsWith(name)) {
661 Node workspaceNode = repoNode.hasNode(workspaceName) ? repoNode
662 .getNode(workspaceName) : repoNode
663 .addNode(workspaceName);
664 distributionElems.add(new DistributionElem(repoElem,
665 workspaceNode));
666 // FIXME remove deleted workspaces
667 }
668 }
669 return distributionElems.toArray();
670 } catch (RepositoryException e) {
671 throw new SlcException("Cannot list workspaces for prefix "
672 + name, e);
673 } finally {
674 JcrUtils.logoutQuietly(session);
675 }
676 }
677
678 public String getLabel() {
679 return name;
680 }
681
682 public String toString() {
683 return getLabel();
684 }
685
686 public void dispose() {
687 }
688
689 public RepoElem getRepoElem() {
690 return repoElem;
691 }
692
693 }
694
695 /** Abstracts a distribution, that is a workspace */
696 private static class DistributionElem extends TreeParent {
697 private final RepoElem repoElem;
698 private final Node workspaceNode;
699
700 /**
701 * Helper to display only version when the workspace name is well
702 * formatted
703 */
704 private static String formatName(Node workspaceNode) {
705 String name = JcrUtils.getNameQuietly(workspaceNode);
706 if (name != null && name.lastIndexOf('-') > 0)
707 return name.substring(name.lastIndexOf('-') + 1);
708 else
709 return name;
710 }
711
712 public DistributionElem(RepoElem repoElem, Node workspaceNode) {
713 super(formatName(workspaceNode));
714 this.repoElem = repoElem;
715 this.workspaceNode = workspaceNode;
716 }
717
718 public Node getWorkspaceNode() {
719 return workspaceNode;
720 }
721
722 public String getWorkspaceName() {
723 return JcrUtils.getNameQuietly(workspaceNode);
724 }
725
726 public String getWorkspacePath() {
727 try {
728 return workspaceNode.getPath();
729 } catch (RepositoryException e) {
730 throw new SlcException("Cannot get or add workspace path "
731 + getWorkspaceName(), e);
732 }
733 }
734
735 public String getRepoPath() {
736 try {
737 return workspaceNode.getParent().getPath();
738 } catch (RepositoryException e) {
739 throw new SlcException("Cannot get or add workspace path "
740 + getWorkspaceName(), e);
741 }
742 }
743
744 public RepoElem getRepoElem() {
745 return repoElem;
746 }
747
748 public Credentials getCredentials() {
749 return repoElem.getCredentials();
750 }
751
752 public boolean isReadOnly() {
753 return repoElem.isReadOnly();
754 }
755 }
756
757 /** Listens to drag */
758 class ViewDragListener extends DragSourceAdapter {
759 public void dragSetData(DragSourceEvent event) {
760 IStructuredSelection selection = (IStructuredSelection) viewer
761 .getSelection();
762 if (selection.getFirstElement() instanceof DistributionElem) {
763 DistributionElem de = (DistributionElem) selection
764 .getFirstElement();
765 if (TextTransfer.getInstance().isSupportedType(event.dataType)) {
766 event.data = de.getWorkspacePath();
767 }
768 }
769 }
770 }
771
772 /** Listens to drop */
773 class ViewDropListener extends ViewerDropAdapter {
774
775 public ViewDropListener(Viewer viewer) {
776 super(viewer);
777 }
778
779 @Override
780 public boolean performDrop(Object data) {
781 DistributionElem sourceDist = (DistributionElem) getSelectedObject();
782 RepoElem targetRepo = (RepoElem) getCurrentTarget();
783
784 Boolean ok = MessageDialog.openConfirm(getSite().getShell(),
785 "Confirm distribution merge", "Do you want to merge "
786 + sourceDist.getWorkspaceName() + " (from repo "
787 + sourceDist.getRepoElem().getLabel()
788 + ") to repo " + targetRepo.getLabel() + "?");
789 if (!ok)
790 return false;
791
792 try {
793 String sourceWorkspace = sourceDist.getWorkspaceName();
794 Repository sourceRepository = RepoUtils.getRepository(
795 repositoryFactory, keyring, sourceDist
796 .getWorkspaceNode().getParent());
797 Credentials sourceCredentials = RepoUtils
798 .getRepositoryCredentials(keyring, sourceDist
799 .getWorkspaceNode().getParent());
800
801 String targetWorkspace = sourceWorkspace;
802 Repository targetRepository = RepoUtils.getRepository(
803 repositoryFactory, keyring, targetRepo.getRepoNode());
804 Credentials targetCredentials = RepoUtils
805 .getRepositoryCredentials(keyring,
806 targetRepo.getRepoNode());
807
808 // Open sessions here since the background thread
809 // won't necessarily be authenticated.
810 // Job should close the sessions.
811 Session sourceSession = sourceRepository.login(
812 sourceCredentials, sourceWorkspace);
813 Session targetSession;
814 try {
815 targetSession = targetRepository.login(targetCredentials,
816 targetWorkspace);
817 } catch (NoSuchWorkspaceException e) {
818 Session defaultSession = targetRepository
819 .login(targetCredentials);
820 try {
821 defaultSession.getWorkspace().createWorkspace(
822 targetWorkspace);
823 } catch (Exception e1) {
824 throw new SlcException("Cannot create new workspace "
825 + targetWorkspace, e);
826 } finally {
827 JcrUtils.logoutQuietly(defaultSession);
828 }
829 targetSession = targetRepository.login(targetCredentials,
830 targetWorkspace);
831 }
832
833 Job workspaceMergeJob = new WorkspaceMergeJob(sourceSession,
834 targetSession);
835 workspaceMergeJob.setUser(true);
836 workspaceMergeJob.schedule();
837 return true;
838 } catch (RepositoryException e) {
839 throw new SlcException("Cannot process drop from " + sourceDist
840 + " to " + targetRepo, e);
841 }
842 }
843
844 @Override
845 public boolean validateDrop(Object target, int operation,
846 TransferData transferType) {
847 if (target instanceof RepoElem) {
848 if (getSelectedObject() instanceof DistributionElem) {
849 // check if not same repository
850 String srcRepoPath = ((DistributionElem) getSelectedObject())
851 .getRepoPath();
852 String targetRepoPath = ((RepoElem) target).getRepoPath();
853 return !targetRepoPath.equals(srcRepoPath);
854 }
855 }
856 return false;
857 }
858 }
859
860 private static class WorkspaceMergeJob extends Job {
861 private Session sourceSession;
862 private Session targetSession;
863
864 public WorkspaceMergeJob(Session sourceSession, Session targetSession) {
865 super("Workspace merge");
866 this.sourceSession = sourceSession;
867 this.targetSession = targetSession;
868 }
869
870 @Override
871 protected IStatus run(IProgressMonitor eclipseMonitor) {
872 long begin = System.currentTimeMillis();
873 try {
874 Query countQuery = sourceSession
875 .getWorkspace()
876 .getQueryManager()
877 .createQuery("select file from [nt:file] as file",
878 Query.JCR_SQL2);
879 QueryResult result = countQuery.execute();
880 Long expectedCount = result.getNodes().getSize();
881 if (log.isDebugEnabled())
882 log.debug("Will copy " + expectedCount + " files...");
883
884 ArgeoMonitor monitor = new EclipseArgeoMonitor(eclipseMonitor);
885 eclipseMonitor
886 .beginTask("Copy files", expectedCount.intValue());
887
888 Long count = JcrUtils.copyFiles(sourceSession.getRootNode(),
889 targetSession.getRootNode(), true, monitor);
890
891 monitor.done();
892 long duration = (System.currentTimeMillis() - begin) / 1000;// in
893 // s
894 if (log.isDebugEnabled())
895 log.debug("Copied " + count + " files in "
896 + (duration / 60) + "min " + (duration % 60) + "s");
897
898 return Status.OK_STATUS;
899 } catch (RepositoryException e) {
900 return new Status(IStatus.ERROR, DistPlugin.ID, "Cannot merge",
901 e);
902 } finally {
903 JcrUtils.logoutQuietly(sourceSession);
904 JcrUtils.logoutQuietly(targetSession);
905 }
906 }
907 }
908
909 /** Listen to double-clicks */
910 private class DistributionsDCL implements IDoubleClickListener {
911
912 public void doubleClick(DoubleClickEvent event) {
913 if (event.getSelection() == null || event.getSelection().isEmpty())
914 return;
915 Object obj = ((IStructuredSelection) event.getSelection())
916 .getFirstElement();
917 if (obj instanceof DistributionElem) {
918 DistributionElem distributionElem = (DistributionElem) obj;
919 DistributionEditorInput dei = new DistributionEditorInput(
920 distributionElem.getName(),
921 getRepositoryDescription(distributionElem.getRepoElem()),
922 distributionElem.getRepoElem().getRepository(),
923 distributionElem.getWorkspaceName(), distributionElem
924 .getCredentials());
925 try {
926 DistPlugin.getDefault().getWorkbench()
927 .getActiveWorkbenchWindow().getActivePage()
928 .openEditor(dei, DistributionEditor.ID);
929 } catch (PartInitException e) {
930 ErrorFeedback.show("Cannot open editor for "
931 + distributionElem.getWorkspaceName(), e);
932 }
933 }
934 }
935 }
936
937 /*
938 * DEPENDENCY INJECTION
939 */
940 public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
941 this.repositoryFactory = repositoryFactory;
942 }
943
944 public void setKeyring(Keyring keyring) {
945 this.keyring = keyring;
946 }
947
948 public void setNodeRepository(Repository repository) {
949 this.nodeRepository = repository;
950 }
951 }