ovirt 前端代码分析

来源:互联网 发布:国外背单词软件 编辑:程序博客网 时间:2024/06/05 11:34

本文重点介绍下ovirt 前端 中的代码架构。本人不是前端开发者,但是ovirt的前面采用的google的GWT的框架Gwtp,前端代码很大一部门是java代码,最后编译成js在clinet的browser中运行。但对于java代码的前台逻辑,每个主tab及每个tab下,每个uiCommand 执行后弹出的popupview的逻辑 过程进行分析 。

首先简单介绍下mvp,其是mvc的m与v与mvc中的指代相同,而p与c类似,负责逻辑的处理。与mvc最大的区别在于view不直接使用model,而是通过Presenter进行交互,m与v分离,model更加高效,view只负责UI定制以及用户的交互。接下来分析代码

GWTP 里用到了gin,类似于ioc,管理对象的实例化。通过注解来实例化对象。我们以vm的tab标签页为例,讲解分析。在

在代码PresenterModule 中定义了每个tab、popubview的 M、V、P三者之间的关系,如下 代码绑定了vm tab标签的P与V之间的关系。 所有的mainTab 的绑定都可以在该类中找到,

// VirtualMachinebindPresenter(MainTabVirtualMachinePresenter.class,MainTabVirtualMachinePresenter.ViewDef.class,MainTabVirtualMachineView.class,MainTabVirtualMachinePresenter.ProxyDef.class);

vm的MainTab 的 view与Presenter绑定,view的构造 方法中,有个参数modelProvider,所有的mainTab的构造方法中的这个参数 都实现 了MainTabModelProvider 这个类。

@Injectpublic MainTabVirtualMachineView(MainModelProvider modelProvider,ApplicationResources resources, ApplicationConstants constants,CommonApplicationConstants commonConstants) {super(modelProvider);this.commonConstants = commonConstants;ViewIdHandler.idHandler.generateAndSetIds(this);initTable(resources, constants);initWidget(getTable());}

view可以通过该类 找到对应的ListModel。过程如下,其中第二个参数mainModelClass 就在每个mainTab对庆的实现类中的构造方法中指定,对应vm为VmListModel.class,而第一个参数CommonModel则在其init方法中初始化了所有的ListModel,最终找到实例化的listModel。

UiCommonModelResolver.getMainListModel(getCommonModel(), mainModelClass);

接下来 介绍下 vm tab的MainModelProvider的 实现。 其实现在VirtualMachineModule 类中定义,通过getVmListProvider方法,返回了一个MainModelProvider的实现。

@Provides@Singletonpublic MainModelProvider getVmListProvider(EventBus eventBus,Provider defaultConfirmPopupProvider,final Provider assignTagsPopupProvider,final Provider makeTemplatePopupProvider,final Provider maintainProvider,final Provider runOncePopupProvider,final Provider changeCDPopupProvider,final Provider exportPopupProvider,final Provider createSnapshotPopupProvider,final Provider migratePopupProvider,final Provider newVmPopupProvider,final Provider newVmListPopupProvider,final Provider addPermissionsPopupProvider,final Provider addBackpermissionsPopupProvider,final Provider guidePopupProvider,final Provider removeConfirmPopupProvider,final Provider vmRemoveConfirmPopupProvider,final Provider reportWindowProvider,final Provider consolePopupProvider,final Provider vncWindoProvider) {return new MainTabModelProvider(eventBus, defaultConfirmPopupProvider, VmListModel.class) {@Overridepublic AbstractModelBoundPopupPresenterWidget<? extends Model, ?> getModelPopup(VmListModel source,UICommand lastExecutedCommand, Model windowModel) {if (lastExecutedCommand == getModel().getAssignTagsCommand()) {return assignTagsPopupProvider.get();} else if (lastExecutedCommand == getModel().getNewTemplateCommand()) {return makeTemplatePopupProvider.get();} else if (lastExecutedCommand == getModel().getMaintainCommand()) {return maintainProvider.get();} else if (lastExecutedCommand == getModel().getRunOnceCommand()) {return runOncePopupProvider.get();} else if (lastExecutedCommand == getModel().getChangeCdCommand()) {return changeCDPopupProvider.get();} else if (lastExecutedCommand == getModel().getExportCommand()) {return exportPopupProvider.get();} else if (lastExecutedCommand == getModel().getCreateSnapshotCommand()) {return createSnapshotPopupProvider.get();} else if (lastExecutedCommand == getModel().getMigrateCommand()) {return migratePopupProvider.get();} else if (lastExecutedCommand == getModel().getNewVmCommand()) {return newVmPopupProvider.get();} else if (lastExecutedCommand == getModel().getNewVmListCommand()) {return newVmListPopupProvider.get();} else if (lastExecutedCommand == getModel().getAddPermissions()) {return addPermissionsPopupProvider.get();} else if (lastExecutedCommand == getModel().getAddBackpermissions()) {return addBackpermissionsPopupProvider.get();} else if (lastExecutedCommand == getModel().getEditCommand()) {return newVmPopupProvider.get();} else if (lastExecutedCommand == getModel().getGuideCommand()) {return guidePopupProvider.get();} else if (windowModel instanceof VncInfoModel) {return vncWindoProvider.get();} else if (lastExecutedCommand == getModel().getEditConsoleCommand()) {return consolePopupProvider.get();}else {return super.getModelPopup(source, lastExecutedCommand, windowModel);}}@Overridepublic AbstractModelBoundPopupPresenterWidget<? extends ConfirmationModel, ?> getConfirmModelPopup(VmListModel source,UICommand lastExecutedCommand) {if (lastExecutedCommand == getModel().getRemoveCommand()) {return vmRemoveConfirmPopupProvider.get();}else if (lastExecutedCommand == getModel().getStopCommand() ||lastExecutedCommand == getModel().getShutdownCommand()) {return removeConfirmPopupProvider.get();} else {return super.getConfirmModelPopup(source, lastExecutedCommand);}}@Overrideprotected ModelBoundPresenterWidget<? extends Model> getModelBoundWidget(UICommand lastExecutedCommand) {if (lastExecutedCommand instanceof ReportCommand) {return reportWindowProvider.get();} else {return super.getModelBoundWidget(lastExecutedCommand);}}};}

匿名内部类,而返回的这个MainTabModelProvider类实现了一个重要的方法getModelPopup (该方法会在指定事件触发后),当前 view就可以通过这个类 的该方法,以及根据上一次执行的方法,找到 p,从而弹出view。例如执行了exportCommand,则弹出VmExportPopupPresenterWidget对应的view,其关系同样在PresenterModule 中定义了。

接下来介绍下 MainTabVirtualMachineView ,对应vmTab的 界面定义,其中对export的button定义如下:

getTable().addActionButton(new WebAdminButtonDefinition(constants.exportVm()) {@Overrideprotected UICommand resolveCommand() {return getMainModel().getExportCommand();}});

定义了一个WebAdminButtion ,在父类的构造方法中会执行如下方法,将UiCommand与该button绑定。

@Overridepublic void update() {// Update command associated with this button definition, this// triggers InitializeEvent when command or its property changessetCommand(resolveCommand());}

而addActionButtion的方法则定义了 click后执行该uiCommand

@Overridepublic void onClick(List selectedItems) {command.execute();}

上面定义的Buffton中的回调方法resolveCommand中,getMainModel 就是根据上面介绍的provider 得到vmListModel,从而得到export的UiCommand。会在Bufftion构造时update方法中执行。

UICommadn的执行

当某一个按钮click后,根据上面的介绍,最后执行VmlistModel的executeCommand,根据uiCommand的对象,最终执行指定的方法,这里对应的是export方法

protected void export(String title){T selectedEntity = (T) getSelectedItem();if (selectedEntity == null){return;}if (getWindow() != null){return;}1) ExportVmModel model = new ExportVmModel();2) setWindow(model);model.startProgress(null);model.setTitle(title);model.setHelpTag(HelpTag.export_virtual_machine);model.setHashName("export_virtual_machine"); //$NON-NLS-1$setupExportModel(model);AsyncDataProvider.getStorageDomainList(new AsyncQuery(this,new INewAsyncCallback() {@Overridepublic void onSuccess(Object target, Object returnValue) {VmBaseListModel vmListModel = (VmBaseListModel) target;List storageDomains =(List) returnValue;List filteredStorageDomains =new ArrayList();for (StorageDomain a : storageDomains){if (a.getStorageDomainType() == StorageDomainType.ImportExport){filteredStorageDomains.add(a);}}vmListModel.postExportGetStorageDomainList(filteredStorageDomains);}}), extractStoragePoolIdNullSafe(selectedEntity));// check, if the VM has a disk which doesn't allow snapshotsendWarningForNonExportableDisks(selectedEntity);}

如上,代码里标注的两步,十分关键,第一步定义了ExportVmModel,第二步执行了监听事件,调用注册的listener。

public void setWindow(Model value){if (window != value){window = value;onPropertyChanged(new PropertyChangedEventArgs("Window")); //$NON-NLS-1$}}@Overrideprotected void onPropertyChanged(PropertyChangedEventArgs e){super.onPropertyChanged(e);getPropertyChangedEvent().raise(this, e);}public void raise(Object sender, EventArgs e){//Iterate on a new instance of listeners list,//to enable listener unsubscribe from event//as a result on event fairing.java.util.ArrayList list = new java.util.ArrayList();for (IEventListener listener : listeners){list.add(listener);}for (IEventListener listener : list){//Update current context.setContext(contexts.containsKey(listener) ? contexts.get(listener) : null);listener.eventRaised(this, sender, e);}}

但是问题来了,这里的Listener是在哪里定义的呢? 接着分析,每个model 的构造方法中,会定义Event,

setPropertyChangedEvent(new Event(ProvidePropertyChangedEvent.definition)); 该事件对应用户的操作。在上面我们介绍过,MainTabModelProvider,在共父类TabModelProvider的构造 方法中,需要注意的有几点,第一 定义了一个popupHandler,其二定义了UiCommand的初始化models。

public TabModelProvider(EventBus eventBus,Provider defaultConfirmPopupProvider) {this.eventBus = eventBus;// Configure UiCommon dialog handlerthis.popupHandler = new ModelBoundPopupHandler(this, eventBus);this.popupHandler.setDefaultConfirmPopupProvider(defaultConfirmPopupProvider);// Add handler to be notified when UiCommon models are (re)initializedeventBus.addHandler(UiCommonInitEvent.getType(), new UiCommonInitHandler() {@Overridepublic void onUiCommonInit(UiCommonInitEvent event) {TabModelProvider.this.onCommonModelChange();}});eventBus.addHandler(CleanupModelEvent.getType(), new CleanupModelEvent.CleanupModelHandler() {@Overridepublic void onCleanupModel(CleanupModelEvent event) {if (hasModel()) {//Setting eventbus to null will also unregister the handlers.getModel().setEventBus(null);}}});}/*** Callback fired when the {@link CommonModel} reference changes.** Override this method to register custom listeners on the corresponding model.*/protected void onCommonModelChange() {// Register dialog model property change listenerpopupHandler.addDialogModelListener(getModel());// Register WidgetModel property change listenergetModel().getPropertyChangedEvent().addListener(new IEventListener() {@Overridepublic void eventRaised(Event ev, Object sender, EventArgs args) {String propName = ((PropertyChangedEventArgs) args).propertyName;if ("WidgetModel".equals(propName)) { //$NON-NLS-1$modelBoundWidgetChange();}}});getModel().setEventBus(getEventBus());}

在上面的法通过pupupHandler 注册了监听者,具体详情如下

/*** Adds a property change listener to the given source model that handles its dialog models.*/public void addDialogModelListener(final M source) {hideAndClearAllPopups();source.getPropertyChangedEvent().addListener(new IEventListener() {@Overridepublic void eventRaised(Event ev, Object sender, EventArgs args) {String propName = ((PropertyChangedEventArgs) args).propertyName;if (windowPropertyNames.contains(propName)) {handleWindowModelChange(source, windowPopup, false, propName);} else if (confirmWindowPropertyNames.contains(propName)) {handleWindowModelChange(source, confirmWindowPopup, true, propName);}}});}

对应handler的windowProertyNames 如下方法。与setwidows 方法 发出的“windows”相同时,

@Overridepublic String[] getWindowPropertyNames() {return new String[] { "Window" }; //$NON-NLS-1$}

执行handleWindowModelChange(source, windowPopup, false, propName); 执行发下逻辑,显然windowModel为window,也就是在事件的源头,setWindow方法中的参数 model。因为pupupResolver为TabProvdier

void handleWindowModelChange(M source, AbstractModelBoundPopupPresenterWidget<?, ?> popup,boolean isConfirm, String propertyName) {Model windowModel = isConfirm ? popupResolver.getConfirmWindowModel(source, propertyName): popupResolver.getWindowModel(source, propertyName);// Reveal new popupif (windowModel != null && popup == null) {// 1. ResolveAbstractModelBoundPopupPresenterWidget<?, ?> newPopup = null;UICommand lastExecutedCommand = source.getLastExecutedCommand();if (windowModel instanceof ConfirmationModel) {// Resolve confirmation popupnewPopup = popupResolver.getConfirmModelPopup(source, lastExecutedCommand);if (newPopup == null && defaultConfirmPopupProvider != null) {// Fall back to basic confirmation popup if possiblenewPopup = defaultConfirmPopupProvider.get();}} else {// Resolve main popupnewPopup = popupResolver.getModelPopup(source, lastExecutedCommand, windowModel);}// 2. Revealif (newPopup != null) {revealAndAssignPopup(windowModel,(AbstractModelBoundPopupPresenterWidget) newPopup,isConfirm);} else {// No popup bound to model, need to clear model reference manuallyif (isConfirm) {popupResolver.clearConfirmWindowModel(source, propertyName);} else {popupResolver.clearWindowModel(source, propertyName);}}}// Hide existing popupelse if (windowModel == null && popup != null) {hideAndClearPopup(popup, isConfirm);}}

从上面的代码的执行逻辑,显然 不是confirm model,因此,执行最关健的一步

newPopup = popupResolver.getModelPopup(source, lastExecutedCommand, windowModel);

也就是上面介绍过的,根据lastExecutedCommand,在Model的executeCommand方法中,会设置last。,找到p,呈现view。

0 0
原创粉丝点击