本章通过一个简单的例子来介绍如何创建一个简单的网络管理系统图形界面。我们把这个小例子分成了多个小的步骤,每个步骤学习一件简单的功能。通过这个例子,我们可以理解TWaver是如何的简单易用,为制作更加复杂的应用打下基础。
本教程面向Java开发者。我们假设读者已经了解基本的Java编程知识以及简单的Swing基础知识。例如如何编写一个简单的窗体、如何编译和运行程序等。当然,如何使用合适的IDE工具、基本的操作系统知识也不在本教程的考虑范围之内。
首先,我们创建一个窗体,然后放入一个分隔条。下面我们开始动手。我们用函数doSample中封装各种TWaver初始化动作:
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import twaver.*;
import twaver.network.*;
import twaver.network.ui.*;
import twaver.table.*;
import twaver.tree.*;
public class Tutorial extends JFrame {
private TDataBox box = new TDataBox("Simple Data Box");
private TNetwork network;
private TTree tree;
private JPanel networkPane = new JPanel(new BorderLayout());
private JPanel treePane = new JPanel(new BorderLayout());
private JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,treePane,networkPane);
public Tutorial() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
getContentPane().add(split, BorderLayout.CENTER);
split.setDividerLocation(100);
doSample();
}
public static void main(String[] args) {
Tutorial frame = new Tutorial();
frame.setSize(500, 300);
frame.setTitle("TWaver Tutorial");
TWaverUtil.centerWindow(frame);
frame.setVisible(true);
}
private void doSample() {
try {
step1();
step2();
step3();
step4();
step5();
step6();
step7();
step8();
step9();
} catch (Exception ex) {
ex.printStackTrace();
}
}
private void step1() {}
private void step2() {}
private void step3() {}
private void step4() {}
private void step5() {}
private void step6() {}
private void step7() {}
private void step8() {}
private void step9() {}
}
运行上述代码,界面如下:
本节介绍如何创建一个简单的拓扑图,显示电信网络数据。本节介绍内容放入函数step1中。
要让拓扑图正常工作,首先需要创建一个DataBox来装载数据。然后创建拓扑图组件与之相连。接下来,把拓扑图放入窗体中。最后,创建两个节点一个连线,放入DataBox。
代码如下:
private void step1() {
network = new TNetwork(box);
networkPane.add(network, BorderLayout.CENTER);
Node nodeA = new Node("A");
nodeA.setName("I'm node A!");
nodeA.setLocation(50, 50);
box.addElement(nodeA);
Node nodeB = new Node("B");
nodeB.setName("I'm node B!");
nodeB.setLocation(200, 200);
box.addElement(nodeB);
Link link = new Link("link", nodeA, nodeB);
link.setName("Telephone Line");
box.addElement(link);
}
运行代码:
创建树组件
这节介绍如何创建一个树组件,用来显示拓扑图中数据的层次结构,也就是其包含关系。一般的应用程序都会用树和拓扑图协同工作,综合显示数据的外观和层次结构。
首先要创建一个tree组件,然后与前面的DataBox实例进行连接。这样,拓扑图和树就共享了一套DataBox中的数据。然后,将树放在窗口分隔条的左侧。这样,树创建就完成了,前面创建的对象会直接显示在树上面。为了生动起见,我们又创建两个Dummy对象,把数据进行了一些分层,使得tree层次更加生动容易理解。
这节代码放置在step2函数中。
private void step2() {
Dummy nodeDummy = new Dummy("node dummy");
nodeDummy.setName("All Nodes");
nodeDummy.addChild(box.getElementByID("A"));
nodeDummy.addChild(box.getElementByID("B"));
box.addElement(nodeDummy);
Dummy linkDummy = new Dummy("link dummy");
linkDummy.setName("All Links");
linkDummy.addChild(box.getElementByID("link"));
box.addElement(linkDummy);
tree = new TTree(box);
JScrollPane scroll = new JScrollPane(tree);
treePane.add(scroll, BorderLayout.CENTER);
}
至此,一个简单的"树-拓扑图"结构的图形界面就完成了,代码非常简单,都是简单的Swing操作,和面向OO的数据对象操作,非常简单易用。但上述界面已经有了一个基本电信网管软件的雏形了。下面我们看如何进一步丰富这个例子。
电信网管系统中,经常要显示电信设备的面板图或者机架图,便于直接监控设备的运行状况。本节介绍如何创建设备面板,并呈现在拓扑图中。我们会用到API和XML两种方式创建数据,供读者参考。
- 用API创建设备面板
要用API创建设备面板,我们首先需要做一些工作:
- 创建一个新的Chassis对象,并将其放入节点A(也就是作为节点A的孩子);
- 创建一个机架对象,放入Chassis对象(作为Chassis对象的孩子);
- 创建一些端口对象,放入机架中(作为机架的孩子);
在编写代码之前,我们需要准备一些图片素材,以便机架图看上去更加生动。如果在读者实际项目中,建议由专业的美工人员进行图片的制作,这样可以让机架图看上去更加的美观、生动;如果不使用图片,也可以直接通过方块等形状来示意,只是美观性稍微差一点。
我们首先准备机架的图片如下图,是一个典型的交换机面板形状:
然后准备第一层端口的以太网口图片(朝上):
第二层端口的以太网口图片(朝下):
以下我们给出了相关代码。按照前面的思路new这些对象,然后放入DataBox中即可。我们创建16个以太网口的交换机,放置在面板的适当位置。这些代码稍微多一点,但是逻辑都极其简单,我们将其放入函数step3:
private void step3() { Node node = (Node) box.getElementByID("A"); Chassis chassis = new Chassis("Chassis A"); node.addChild(chassis); box.addElement(chassis); //1.在chassis上添加机架 Rack rack = new Rack("Rack A"); rack.setName("Rack"); rack.setLocation(50, 50); rack.setImage("/demo/resource/tutorial/rack.png"); chassis.addChild(rack); box.addElement(rack); //2.在机架上添加端口 String imgPort1 = "/demo/resource/tutorial/port1.png"; String imgPort2 = "/demo/resource/tutorial/port2.png"; for (int module = 0; module < 4; module++) { Dummy dummy = new Dummy("PortDummy" + module); dummy.setName("module" + module); rack.addChild(dummy); box.addElement(dummy); for (int index = 0; index < 4; index++) { Port port = new Port(module + ":" + index); int x, y; if (module % 2 == 0) { x = 210 + index * 24; } else { x = 319 + index * 24; } if (module < 2) { y = 16; port.setImage(imgPort1); } else { y = 37; port.setImage(imgPort2); } x += rack.getLocation().x; y += rack.getLocation().y; port.setLocation(new Point(x, y)); dummy.addChild(port); box.addElement(port); } } }
好了。我们在拓扑图上用鼠标双击节点A,拓扑图会自动进入我们上面创建的机架图;双击拓扑图的空白区域,可以返回上层拓扑图。节点A作为设备节点,Chassis对象代表一个机架图,Rack代表物理机架,而这些端口对象则代表了设备上的具体以太网端口。
由此可见,TWaver还是很简单的。以上代码量并不多,但是一个具有拓扑图、机架图、树图的电信网管系统雏形已经有模有样了。这就是使用TWaver带来的巨大开发效率。
- 用XML创建机架图
上一节介绍了使用API编程的方式用代码来创建机架图。由于每个设备的外观、结构千差万别,如果正在开发一套综合网管系统,需要管理上百种不同厂商的设备,则这个工作就显得非常啰嗦,代码量很大,也不容易维护。这时,可以考虑用XML的方式来简化代码。这节我们同样做上一节所做的工作,只是换成了XML的方式。
首先我们准备设备面板的模版文件,也就是XML文件。我们可以通过TWaver产品Demo提供的编辑器进行编辑,然后导出。为了简化,我们直接给出和上节面板相同数据的XML文件,如下:
<?xml version="1.0" encoding="UTF-8"?> <java version="1.4.1" class="java.beans.XMLDecoder"> <!-- rack --> <object class="twaver.Rack" id="rack"> <void property="Image"> <string>/image/twaverRack.png</string></void> <void property="location"> <object class="java.awt.Point"> <int>100</int> <int>100</int> </object> </void> </object> <!-- ports --> <!-- console port --> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverConsolePort.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>259</int><int>145</int> </object> </void> </object> <!-- fiber port --> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverFiberPort.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>582</int><int>132</int> </object> </void> </object> <!-- module 1--> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort1.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>310</int><int>116</int> </object> </void> </object> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort1.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>334</int><int>116</int> </object> </void> </object> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort1.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>358</int><int>116</int> </object> </void> </object> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort1.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>382</int><int>116</int> </object> </void> </object> <!-- module 2--> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort1.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>419</int><int>116</int> </object> </void> </object> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort1.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>443</int><int>116</int> </object> </void> </object> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort1.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>467</int><int>116</int> </object> </void> </object> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort1.png</string> </void> <void property="parent"> <object idref="rack"/></void> <void property="location"> <object class="java.awt.Point"> <int>491</int><int>116</int> </object> </void> </object> <!-- module 3--> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort2.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>310</int><int>137</int> </object> </void> </object> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort2.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>334</int><int>137</int> </object> </void> </object> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort2.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>358</int><int>137</int> </object> </void> </object> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort2.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>382</int><int>137</int> </object> </void> </object> <!-- module 4--> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort2.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>419</int><int>137</int> </object> </void> </object> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort2.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>443</int><int>137</int> </object> </void> </object> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort2.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>467</int><int>137</int> </object> </void> </object> <object class="twaver.Port"> <void property="Image"> <string>/image/twaverPort2.png</string> </void> <void property="parent"> <object idref="rack"/> </void> <void property="location"> <object class="java.awt.Point"> <int>491</int><int>137</int> </object> </void> </object> </java>
然后,我们把它保存在文件equipment1_template.xml中。最后,通过以下代码进行加载:
private void step3() { //add chassis to node A. Node node = (Node) box.getElementByID("A"); Chassis chassis = new Chassis("Chassis A"); chassis.setDataSource("equipment1_template.xml"); node.addChild(chassis); box.addElement(chassis); }
上述代码还是创建一个Chassis对象,但是把XML模版文件名直接作为属性,设置给Chassis对象即可。具体机架图上的数据不再需要用代码创建。直接运行上述代码,可以看到和上一节一模一样的效果。可见,适当的采用XML方式,可以大幅的减少代码工作量;同时,这个XML也便于修改、维护、存储、交换。
注意:上述代码运行时,在双击节点A之前,机架图数据实际并未加载进DataBox中;当鼠标第一次双击节点A时,TWaver检查Chassis状态是否数据已经加载,如果未加载则从XML中进行读取解析加载,否则直接显示机架图内容。这也是TWaver另外一个重要特性:延迟加载机制。关于延迟加载方面的细节,请看后续章节介绍。
可见,通过XML可以获得如下一些好处:
- 通过XML和延迟加载机制,可以加速程序启动速度。想象一下,如果有1000个节点,每个节点都有一套复杂的机架图,则完全通过API来加载,程序启动将非常缓慢,也没有必要在程序启动就创建所有的数据。通过延迟加载,首先显示必须显示的节点数据,当双击查看下层面板细节时,再通过XML获取细节数据。这样可以大幅优化程序的效率。
- 简化代码:把相对固定的数据放入XML作为数据模版,可以大幅减少代码的编写数量,便于维护、存储、修改、交换。
- 降低内存消耗:通过XML实现"按需加载"的机制,只创建需要的数据,可以节省内存消耗,降低数据的创建数量。
- 数据共享:通过定义数据模版,可以实现数据在前后台、多程序间的共享。