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