blob: 4b5a2abe9951bf77fed135aa3b464e5423d5c71f [file] [log] [blame]
Richard S. Hall5c23fb92006-09-28 19:55:37 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
Richard S. Hall54d1e2e2006-04-05 14:19:38 +00009 *
Richard S. Hall5c23fb92006-09-28 19:55:37 +000010 * http://www.apache.org/licenses/LICENSE-2.0
Richard S. Hall54d1e2e2006-04-05 14:19:38 +000011 *
Richard S. Hall5c23fb92006-09-28 19:55:37 +000012 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
Richard S. Hall54d1e2e2006-04-05 14:19:38 +000018 */
19package org.apache.felix.shell.gui.plugin;
20
21import java.awt.*;
22import java.awt.event.ActionEvent;
23import java.awt.event.ActionListener;
24import java.io.PrintStream;
25import java.net.URL;
26import java.util.*;
27
28import javax.swing.*;
29import javax.swing.event.TreeSelectionEvent;
30import javax.swing.event.TreeSelectionListener;
31import javax.swing.tree.*;
32
33import org.apache.felix.shell.gui.Plugin;
34import org.osgi.framework.*;
35import org.osgi.service.obr.*;
36
37public class OBRPlugin extends JPanel implements Plugin
38{
39 private static final String DEPLOY_BUTTON = "Deploy";
40 private static final String START_BUTTON = "Deploy & start";
41
42 private BundleContext m_context = null;
43 private ServiceReference m_repoRef = null;
44 private RepositoryAdmin m_repoAdmin = null;
45
46 private SimpleTreeNode m_rootNode = null;
47 private CreateRootRunnable m_createRootRunnable = new CreateRootRunnable();
48 private SetRootRunnable m_setRootRunnable = new SetRootRunnable();
49
50 private JButton m_addRepoButton = null;
51 private JButton m_removeRepoButton = null;
52 private JButton m_refreshRepoButton = null;
53 private JTree m_bundleTree = null;
54 private JButton m_deployButton = null;
55 private JButton m_startButton = null;
56 private JButton m_infoButton = null;
57 private ScrollableOutputArea m_soa = null;
58 private JButton m_clearButton = null;
59
60 private PrintStream m_out = null;
61
62 // Plugin interface methods.
63
64 public String getName()
65 {
66 return "OBR";
67 }
68
69 public Component getGUI()
70 {
71 return this;
72 }
73
74 // Implementation.
75
76 public OBRPlugin(BundleContext context)
77 {
78 m_context = context;
79 m_out = new PrintStream(
80 new OutputAreaStream(
81 m_soa = new ScrollableOutputArea(5, 30)));
82
83 // Create the gui.
84 createUserInterface();
85
86 synchronized (this)
87 {
88 // Listen for registering/unregistering bundle repository services.
89 ServiceListener sl = new ServiceListener() {
90 public void serviceChanged(ServiceEvent event)
91 {
92 synchronized (OBRPlugin.this)
93 {
94 // Ignore additional services if we already have one.
95 if ((event.getType() == ServiceEvent.REGISTERED)
96 && (m_repoRef != null))
97 {
98 return;
99 }
100 // Initialize the service if we don't have one.
101 else if ((event.getType() == ServiceEvent.REGISTERED)
102 && (m_repoRef == null))
103 {
104 lookupService();
105 }
106 // Unget the service if it is unregistering.
107 else if ((event.getType() == ServiceEvent.UNREGISTERING)
108 && event.getServiceReference().equals(m_repoRef))
109 {
110 m_context.ungetService(m_repoRef);
111 m_repoRef = null;
112 m_repoAdmin = null;
113 // Try to get another service.
114 lookupService();
115 }
116 }
117 }
118 };
119
120 try
121 {
122 m_context.addServiceListener(sl,
123 "(objectClass=" + RepositoryAdmin.class.getName() + ")");
124 }
125 catch (InvalidSyntaxException ex)
126 {
127 System.err.println("OBRPlugin: " + ex);
128 }
129
130 // Now try to manually initialize the shell service
131 // since one might already be available.
132 lookupService();
133 }
134 }
135
136 private synchronized void lookupService()
137 {
138 if (m_repoAdmin != null)
139 {
140 return;
141 }
142 m_repoRef = m_context.getServiceReference(RepositoryAdmin.class.getName());
143 if (m_repoRef == null)
144 {
145 }
146 else
147 {
148 m_repoAdmin = (RepositoryAdmin) m_context.getService(m_repoRef);
149 }
150
151 // Update the model.
152 initializeRootNode();
153 }
154
155 private void createEventListeners()
156 {
157 // Create action listeners.
158 m_addRepoButton.addActionListener(new ActionListener() {
159 public void actionPerformed(ActionEvent event)
160 {
161 synchronized (OBRPlugin.this)
162 {
163 if (m_repoAdmin == null)
164 {
165 return;
166 }
167
168 String s = JOptionPane.showInputDialog(
169 OBRPlugin.this, "Enter repository URL:");
170
171 if (s != null)
172 {
173 try
174 {
175 m_repoAdmin.addRepository(new URL(s));
176 }
177 catch (Exception ex)
178 {
179 ex.printStackTrace();
180 }
181 }
182
183 // Update the table.
184 initializeRootNode();
185 }
186 }
187 });
188
189 m_removeRepoButton.addActionListener(new ActionListener() {
190 public void actionPerformed(ActionEvent event)
191 {
192 synchronized (OBRPlugin.this)
193 {
194 if (m_repoAdmin == null)
195 {
196 return;
197 }
198
199 TreePath[] paths = m_bundleTree.getSelectionPaths();
200 for (int i = 0; i < paths.length; i++)
201 {
202 SimpleTreeNode node = (SimpleTreeNode) paths[i].getLastPathComponent();
203 if (node.getObject() instanceof Repository)
204 {
205 m_repoAdmin.removeRepository(
206 ((Repository)
207 ((SimpleTreeNode)
208 paths[i].getLastPathComponent()).getObject()).getURL());
209 }
210 }
211
212 // Update the table.
213 initializeRootNode();
214 }
215 }
216 });
217
218 m_refreshRepoButton.addActionListener(new ActionListener() {
219 public void actionPerformed(ActionEvent event)
220 {
221 synchronized (OBRPlugin.this)
222 {
223 if (m_repoAdmin == null)
224 {
225 return;
226 }
227
228 TreePath[] paths = m_bundleTree.getSelectionPaths();
229 for (int i = 0; i < paths.length; i++)
230 {
231 SimpleTreeNode node = (SimpleTreeNode) paths[i].getLastPathComponent();
232 if (node.getObject() instanceof Repository)
233 {
234 try
235 {
236 // Adding the repository again causes it to be reparsed.
237 m_repoAdmin.addRepository(
238 ((Repository) node.getObject()).getURL());
239 } catch (Exception ex)
240 {
241 ex.printStackTrace();
242 }
243 }
244 }
245
246 // Update the table.
247 initializeRootNode();
248 }
249 }
250 });
251
252 ActionListener al = new ActionListener() {
253 public void actionPerformed(ActionEvent event)
254 {
255 boolean start = event.getActionCommand().equals(START_BUTTON);
256
257 synchronized (OBRPlugin.this)
258 {
259 if (m_repoAdmin == null)
260 {
261 return;
262 }
263
264 Resolver resolver = m_repoAdmin.resolver();
265 TreePath[] paths = m_bundleTree.getSelectionPaths();
266 for (int i = 0; i < paths.length; i++)
267 {
268 SimpleTreeNode node = (SimpleTreeNode) paths[i].getLastPathComponent();
269 if (node.getObject() instanceof Resource)
270 {
271 resolver.add((Resource) node.getObject());
272 }
273 }
274
275 if ((resolver.getAddedResources() != null) &&
276 (resolver.getAddedResources().length > 0))
277 {
278 if (resolver.resolve())
279 {
280 m_out.println("Target resource(s):");
281 printUnderline(m_out, 19);
282 Resource[] resources = resolver.getAddedResources();
283 for (int resIdx = 0; (resources != null) && (resIdx < resources.length); resIdx++)
284 {
285 m_out.println(" " + resources[resIdx].getPresentationName()
286 + " (" + resources[resIdx].getVersion() + ")");
287 }
288 resources = resolver.getRequiredResources();
289 if ((resources != null) && (resources.length > 0))
290 {
291 m_out.println("\nRequired resource(s):");
292 printUnderline(m_out, 21);
293 for (int resIdx = 0; resIdx < resources.length; resIdx++)
294 {
295 m_out.println(" " + resources[resIdx].getPresentationName()
296 + " (" + resources[resIdx].getVersion() + ")");
297 }
298 }
299 resources = resolver.getOptionalResources();
300 if ((resources != null) && (resources.length > 0))
301 {
302 m_out.println("\nOptional resource(s):");
303 printUnderline(m_out, 21);
304 for (int resIdx = 0; resIdx < resources.length; resIdx++)
305 {
306 m_out.println(" " + resources[resIdx].getPresentationName()
307 + " (" + resources[resIdx].getVersion() + ")");
308 }
309 }
310
311 try
312 {
313 m_out.print("\nDeploying...");
314 resolver.deploy(start);
315 m_out.println("done.");
316 }
317 catch (IllegalStateException ex)
318 {
319 m_out.println(ex);
320 }
321 }
322 else
323 {
324 Requirement[] reqs = resolver.getUnsatisfiedRequirements();
325 if ((reqs != null) && (reqs.length > 0))
326 {
327 m_out.println("Unsatisfied requirement(s):");
328 printUnderline(m_out, 27);
329 for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++)
330 {
331 m_out.println(" " + reqs[reqIdx].getFilter());
332 Resource[] resources = resolver.getResources(reqs[reqIdx]);
333 for (int resIdx = 0; resIdx < resources.length; resIdx++)
334 {
335 m_out.println(" " + resources[resIdx].getPresentationName());
336 }
337 }
338 }
339 else
340 {
341 m_out.println("Could not resolve targets.");
342 }
343 }
344
345 m_out.println("");
346 }
347 }
348 }
349 };
350
351 m_deployButton.addActionListener(al);
352 m_startButton.addActionListener(al);
353
354 m_infoButton.addActionListener(new ActionListener() {
355 public void actionPerformed(ActionEvent event)
356 {
357 synchronized (OBRPlugin.this)
358 {
359 if (m_repoAdmin == null)
360 {
361 return;
362 }
363 TreePath[] paths = m_bundleTree.getSelectionPaths();
364 for (int i = 0; i < paths.length; i++)
365 {
366 if (i != 0)
367 {
368 m_out.println("");
369 }
370 printInfo(m_out,
371 ((SimpleTreeNode) paths[i].getLastPathComponent()).getObject());
372 }
373 m_out.println("");
374 }
375 }
376 });
377
378 m_clearButton.addActionListener(new ActionListener() {
379 public void actionPerformed(ActionEvent event)
380 {
381 synchronized (OBRPlugin.this)
382 {
383 m_soa.setText("");
384 }
385 }
386 });
387
388 m_bundleTree.addTreeSelectionListener(new TreeSelectionListener() {
389 public void valueChanged(TreeSelectionEvent e)
390 {
391 if (m_repoAdmin == null)
392 {
393 return;
394 }
395 TreePath[] paths = m_bundleTree.getSelectionPaths();
396 boolean repoOnly = true;
397 if (paths != null)
398 {
399 for (int i = 0; repoOnly && (i < paths.length); i++)
400 {
401 SimpleTreeNode node = (SimpleTreeNode) paths[i].getLastPathComponent();
402 if (!(node.getObject() instanceof Repository))
403 {
404 repoOnly = false;
405 }
406 }
407 }
408 m_removeRepoButton.setEnabled((paths != null) && repoOnly);
409 m_refreshRepoButton.setEnabled((paths != null) && repoOnly);
410 m_infoButton.setEnabled((paths != null) && (paths.length > 0));
411 }
412 });
413 }
414
415 private void printInfo(PrintStream out, Object obj)
416 {
417 if (obj != null)
418 {
419 if (obj instanceof Repository)
420 {
421 Repository repo = (Repository) obj;
422 out.println(repo.getName());
423 out.println(" URL = " + repo.getURL());
424 out.println(" Modified = " + new Date(repo.getLastModified()));
425 }
426 else if (obj instanceof Resource)
427 {
428 Resource res = (Resource) obj;
429 out.println(res.getPresentationName());
430
431 // Print properties.
432 Map map = res.getProperties();
433 Iterator iter = map.entrySet().iterator();
434 while (iter.hasNext())
435 {
436 Map.Entry entry = (Map.Entry) iter.next();
437 out.println(" " + entry.getKey() + " = " + entry.getValue());
438 }
439
440 // Print requirements.
441 Requirement[] reqs = res.getRequirements();
442 for (int i = 0; (reqs != null) && (i < reqs.length); i++)
443 {
444 if (i == 0)
445 {
446 out.println(" requirements:");
447 }
448 out.println(" " + reqs[i].getFilter());
449 }
450
451 // Print capabilities.
452 Capability[] caps = res.getCapabilities();
453 for (int i = 0; (caps != null) && (i < caps.length); i++)
454 {
455 if (i == 0)
456 {
457 out.println(" capabilities:");
458 }
459 out.println(" " + caps[i].getName() + " = " + caps[i].getProperties());
460 }
461 }
462 }
463 }
464
465 private void createUserInterface()
466 {
467 JSplitPane split = new JSplitPane(
468 JSplitPane.VERTICAL_SPLIT, createTree(), createConsole());
469 split.setResizeWeight(1.0);
470 split.setDividerSize(5);
471 setLayout(new BorderLayout());
472 add(split, BorderLayout.CENTER);
473 createEventListeners();
474 }
475
476 private JPanel createTree()
477 {
478 JPanel panel = new JPanel(new BorderLayout());
479 panel.add(createRepoPanel(), BorderLayout.NORTH);
480 panel.add(createResourcePanel(), BorderLayout.CENTER);
481 return panel;
482 }
483
484 private JPanel createRepoPanel()
485 {
486 JPanel panel = new JPanel();
487 panel.setBorder(BorderFactory.createTitledBorder("Repositories"));
488 panel.add(m_addRepoButton = new JButton("Add"));
489 m_addRepoButton.setMnemonic('A');
490 panel.add(m_removeRepoButton = new JButton("Remove"));
491 m_removeRepoButton.setMnemonic('R');
492 panel.add(m_refreshRepoButton = new JButton("Refresh"));
493 m_refreshRepoButton.setMnemonic('f');
494 m_removeRepoButton.setEnabled(false);
495 m_refreshRepoButton.setEnabled(false);
496 return panel;
497 }
498
499 private JPanel createResourcePanel()
500 {
501 JPanel panel = new JPanel(new BorderLayout());
502 panel.setBorder(BorderFactory.createTitledBorder("Resources"));
503 JScrollPane scroll = null;
504 panel.add(
505 scroll = new JScrollPane(
506 m_bundleTree = new JTree(new SimpleTreeNode(null, null))), BorderLayout.CENTER);
507 panel.add(createButtonPanel(), BorderLayout.SOUTH);
508
509 // Set table scroll pane to reasonable size.
510 scroll.setPreferredSize(new Dimension(100, 100));
511 m_bundleTree.setMinimumSize(new Dimension(0, 0));
512
513 // We don't need to see the root.
514 m_bundleTree.setRootVisible(false);
515 m_bundleTree.setShowsRootHandles(true);
516
517 return panel;
518 }
519
520 private JPanel createButtonPanel()
521 {
522 JPanel panel = new JPanel();
523 panel.add(m_deployButton = new JButton(DEPLOY_BUTTON));
524 panel.add(m_startButton = new JButton(START_BUTTON));
525 panel.add(m_infoButton = new JButton("Info"));
526 m_deployButton.setMnemonic('D');
527 m_startButton.setMnemonic('S');
528 m_infoButton.setMnemonic('I');
529 m_infoButton.setEnabled(false);
530 return panel;
531 }
532
533 private JPanel createConsole()
534 {
535 JPanel panel = new JPanel(new BorderLayout());
536 panel.add(m_soa, BorderLayout.CENTER);
537 panel.add(m_clearButton = new JButton("Clear"), BorderLayout.EAST);
538 m_clearButton.setMnemonic('C');
539
540 return panel;
541 }
542
543 private static void printUnderline(PrintStream out, int length)
544 {
545 for (int i = 0; i < length; i++)
546 {
547 out.print('-');
548 }
549 out.println("");
550 }
551
552 private void initializeRootNode()
553 {
554 synchronized (m_createRootRunnable)
555 {
556 new Thread(m_createRootRunnable).start();
557 }
558 }
559
560 private class CreateRootRunnable implements Runnable
561 {
562 public void run()
563 {
564 synchronized (OBRPlugin.this)
565 {
566 // HACK ALERT: This next if statement is a hack to force
567 // the OBR service to retrieve its repository files on
568 // this thread, rather than the Swing thread. This hack
569 // assumes that this GUI is working with Felix' OBR service,
570 // which defers retrieving repository URLs until needed.
571 if (m_repoAdmin != null)
572 {
573 m_repoAdmin.listRepositories();
574 }
575
576 // Create the new root node and then set it.
577 m_rootNode = new SimpleTreeNode(null, m_repoAdmin);
578 try
579 {
580 SwingUtilities.invokeAndWait(m_setRootRunnable);
581 }
582 catch (Exception ex)
583 {
584 // Ignore.
585 }
586 }
587 }
588 }
589
590 private class SetRootRunnable implements Runnable
591 {
592 public void run()
593 {
594 ((DefaultTreeModel) m_bundleTree.getModel()).setRoot(m_rootNode);
595 }
596 }
597
598 private static class SimpleTreeNode implements TreeNode
599 {
600 private TreeNode m_parent = null;
601 private Object m_obj = null;
602 private TreeNode[] m_children = null;
603 private String m_toString = null;
604
605 public SimpleTreeNode(TreeNode parent, Object obj)
606 {
607 m_parent = parent;
608 m_obj = obj;
609 }
610
611 public Object getObject()
612 {
613 return m_obj;
614 }
615
616 public TreeNode getChildAt(int index)
617 {
618 if (m_children == null)
619 {
620 initialize();
621 }
622
623 if ((m_children != null) && (index >= 0) && (index < m_children.length))
624 {
625 return m_children[index];
626 }
627
628 return null;
629 }
630
631 public int getChildCount()
632 {
633 if (m_children == null)
634 {
635 initialize();
636 }
637 return (m_children == null) ? 0 : m_children.length;
638 }
639
640 public TreeNode getParent()
641 {
642 return m_parent;
643 }
644
645 public int getIndex(TreeNode node)
646 {
647 if (m_children == null)
648 {
649 initialize();
650 }
651 for (int i = 0; (m_children != null) && (i < m_children.length); i++)
652 {
653 if (m_children[i] == node)
654 {
655 return i;
656 }
657 }
658 return -1;
659 }
660
661 public boolean getAllowsChildren()
662 {
663 return true;
664 }
665
666 public boolean isLeaf()
667 {
668 return (getChildCount() == 0);
669 }
670
671 public Enumeration children()
672 {
673 return null;
674 }
675
676 private void initialize()
677 {
678 // The current node might be the root repository admin.
679 if ((m_obj != null) && (m_obj instanceof RepositoryAdmin))
680 {
681 Object[] objs = ((RepositoryAdmin) m_obj).listRepositories();
682 if (objs != null)
683 {
684 m_children = new TreeNode[objs.length];
685 for (int i = 0; i < objs.length; i++)
686 {
687 m_children[i] = new SimpleTreeNode(this, objs[i]);
688 }
689 }
690 }
691 else if (m_obj instanceof Repository)
692 {
693 Object[] objs = ((Repository) m_obj).getResources();
694 if (objs != null)
695 {
696 m_children = new TreeNode[objs.length];
697 for (int i = 0; i < objs.length; i++)
698 {
699 m_children[i] = new SimpleTreeNode(this, objs[i]);
700 }
701 }
702 }
703 }
704
705 public String toString()
706 {
707 if (m_toString == null)
708 {
709 if (m_obj instanceof RepositoryAdmin)
710 {
711 m_toString = "ROOT";
712 }
713 else if (m_obj instanceof Repository)
714 {
715 m_toString = ((Repository) m_obj).getName();
716 }
717 else if (m_obj instanceof Resource)
718 {
719 m_toString = ((Resource) m_obj).getPresentationName()
720 + " (" + ((Resource) m_obj).getVersion() + ")";
721 }
722 else
723 {
724 m_toString = (m_obj != null) ? m_obj.toString() : "null";
725 }
726 }
727 return m_toString;
728 }
729 }
730}