ovirt gwt 代码分析

本文重点介绍下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 的绑定都可以在该类中找到,

// VirtualMachine
bindPresenter(MainTabVirtualMachinePresenter.class,
                MainTabVirtualMachinePresenter.ViewDef.class,
                MainTabVirtualMachineView.class,
                MainTabVirtualMachinePresenter.ProxyDef.class);

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

 @Inject
    public MainTabVirtualMachineView(MainModelProvider<VM, VmListModel> 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
    @Singleton
    public MainModelProvider<VM, VmListModel> getVmListProvider(EventBus eventBus,
            Provider<DefaultConfirmationPopupPresenterWidget> defaultConfirmPopupProvider,
            final Provider<AssignTagsPopupPresenterWidget> assignTagsPopupProvider,
            final Provider<VmMakeTemplatePopupPresenterWidget> makeTemplatePopupProvider,
            final Provider<VmMaintainPopupPresenterWidget> maintainProvider,
            final Provider<VmRunOncePopupPresenterWidget> runOncePopupProvider,
            final Provider<VmChangeCDPopupPresenterWidget> changeCDPopupProvider,
            final Provider<VmExportPopupPresenterWidget> exportPopupProvider,
            final Provider<VmSnapshotCreatePopupPresenterWidget> createSnapshotPopupProvider,
            final Provider<VmMigratePopupPresenterWidget> migratePopupProvider,
            final Provider<VmPopupPresenterWidget> newVmPopupProvider,
            final Provider<VmListPopupPresenterWidget> newVmListPopupProvider,
            final Provider<VmAddPermissionsPopupPresenterWidget> addPermissionsPopupProvider,
            final Provider<VmAddBackpermissionsPopupPresenterWidget> addBackpermissionsPopupProvider,
            final Provider<GuidePopupPresenterWidget> guidePopupProvider,
            final Provider<RemoveConfirmationPopupPresenterWidget> removeConfirmPopupProvider,
            final Provider<VmRemovePopupPresenterWidget> vmRemoveConfirmPopupProvider,
            final Provider<ReportPresenterWidget> reportWindowProvider,
            final Provider<ConsolePopupPresenterWidget> consolePopupProvider,
            final Provider<VncInfoPopupPresenterWidget> vncWindoProvider) {
        return new MainTabModelProvider<VM, VmListModel>(eventBus, defaultConfirmPopupProvider, VmListModel.class) {
            @Override
            public 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);
                }
            }

            @Override
            public 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);
                }
            }

            @Override
            protected 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<VM>(constants.exportVm()) {
            @Override
            protected UICommand resolveCommand() {
                return getMainModel().getExportCommand();
            }
        });

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

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

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

@Override
    public void onClick(List<T> 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() {
                    @Override
                    public void onSuccess(Object target, Object returnValue) {
                        VmBaseListModel vmListModel = (VmBaseListModel) target;
                        List<StorageDomain> storageDomains =
                                (List<StorageDomain>) returnValue;

                        List<StorageDomain> filteredStorageDomains =
                                new ArrayList<StorageDomain>();
                        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 snapshot
        sendWarningForNonExportableDisks(selectedEntity);
    }

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

public void setWindow(Model value)
    {
        if (window != value)
        {
            window = value;
            onPropertyChanged(new PropertyChangedEventArgs("Window")); //$NON-NLS-1$
        }
    }
 @Override
    protected 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<IEventListener> list = new java.util.ArrayList<IEventListener>();
        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<DefaultConfirmationPopupPresenterWidget> defaultConfirmPopupProvider) {
        this.eventBus = eventBus;

        // Configure UiCommon dialog handler
        this.popupHandler = new ModelBoundPopupHandler<M>(this, eventBus);
        this.popupHandler.setDefaultConfirmPopupProvider(defaultConfirmPopupProvider);

        // Add handler to be notified when UiCommon models are (re)initialized
        eventBus.addHandler(UiCommonInitEvent.getType(), new UiCommonInitHandler() {
            @Override
            public void onUiCommonInit(UiCommonInitEvent event) {
                TabModelProvider.this.onCommonModelChange();
            }
        });
        eventBus.addHandler(CleanupModelEvent.getType(), new CleanupModelEvent.CleanupModelHandler() {

            @Override
            public 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.
     * <p>
     * Override this method to register custom listeners on the corresponding model.
     */
    protected void onCommonModelChange() {
        // Register dialog model property change listener
        popupHandler.addDialogModelListener(getModel());

        // Register WidgetModel property change listener
        getModel().getPropertyChangedEvent().addListener(new IEventListener() {
            @Override
            public 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() {
            @Override
            public 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”相同时,

 @Override
    public 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 popup
        if (windowModel != null && popup == null) {
            // 1. Resolve
            AbstractModelBoundPopupPresenterWidget<?, ?> newPopup = null;
            UICommand lastExecutedCommand = source.getLastExecutedCommand();

            if (windowModel instanceof ConfirmationModel) {
                // Resolve confirmation popup
                newPopup = popupResolver.getConfirmModelPopup(source, lastExecutedCommand);

                if (newPopup == null && defaultConfirmPopupProvider != null) {
                    // Fall back to basic confirmation popup if possible
                    newPopup = defaultConfirmPopupProvider.get();
                }
            } else {
                // Resolve main popup
                newPopup = popupResolver.getModelPopup(source, lastExecutedCommand, windowModel);
            }

            // 2. Reveal
            if (newPopup != null) {
                revealAndAssignPopup(windowModel,
                        (AbstractModelBoundPopupPresenterWidget<Model, ?>) newPopup,
                        isConfirm);
            } else {
                // No popup bound to model, need to clear model reference manually
                if (isConfirm) {
                    popupResolver.clearConfirmWindowModel(source, propertyName);
                } else {
                    popupResolver.clearWindowModel(source, propertyName);
                }
            }
        }

        // Hide existing popup
        else if (windowModel == null && popup != null) {
            hideAndClearPopup(popup, isConfirm);
        }
    }

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

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

也就是上面介绍过的,根据lastExecutedCommand,在Model的executeCommand方法中,会设置last。,找到p,  而popupResolver则是 ModelProvider, 其在VirtualMachineModel中定义 。比如 我们创建一个快照,则会返回如下

VmSnapshotCreatePopupPresenterWidget   ,在找到P后,上述代码 第二步,也就是如何呈现所对应的view。  就是 revealAndAssignPopup方法,其中第一个参数windowModel 就是 事件原model中的对应的uiCommand执行时,setWindows时的那个新new的 model的M,而第二个参数newPopup则是将要弹出的presenter对应的P。

/**
     * Reveals a popup bound to the given model.
     */
    <T extends Model> void revealPopup(final T model,
            final AbstractModelBoundPopupPresenterWidget<T, ?> popup) {
        assert (model != null) : "Popup model must not be null"; //$NON-NLS-1$

        // Initialize popup
        popup.init(model);

        // Add "PROGRESS" property change handler to Window model
        model.getPropertyChangedEvent().addListener(new IEventListener() {
            @Override
            public void eventRaised(Event ev, Object sender, EventArgs args) {
                PropertyChangedEventArgs pcArgs = (PropertyChangedEventArgs) args;

                if (PropertyChangedEventArgs.Args.PROGRESS.toString().equals(pcArgs.propertyName)) { //$NON-NLS-1$
                    updatePopupProgress(model, popup);
                }
            }
        });
        updatePopupProgress(model, popup);

        // Reveal popup
        RevealRootPopupContentEvent.fire(eventBus, popup);
    }

 

 

    1)根据model初始化View , 接着会执行 P的 init(T m)方法,在这个方法中就体现了MVP的思想,v与m不直接交互,而是通过P来进行。首先定义通用的property。 会对model的 PropertyChangedEvent 增加 定义的listener,而listener会根据不同的事件参数的propertyName本执行不同的逻辑,每个listener只会对自己感兴趣的property。而解发事件的Action有很多,比如上面得到p的 setWindows(M) 方法,以及涉及到setItems,setSeletectedItmes等,不同的根据不同的model而不同。

    2)而后 addFooterButtons方法,或者 model的Command为空时,也就是要等待initialize方法后,在这里注册command的增加事件,删除buttion,重新根据Command定义button,并和Command绑定。并更新 tabInex。 简单来说这一步所做的就是定义ok 与Cancel的 button.

    3)接着同样定义Poupup handler 的事件

    4) RevealRootPopupContentEvent.fire(eventBus, popup);这一步灰常 关键,定义界面的显示,前后做的一些操作,比如说 P 或者V 中的 onReveal()都是由该事件 引出的,一个生命周期内的方法。

通过SimpleEventBus,发布事件,而后处理事件时,调用event.dispatch(handler);而这个Handler是在Event中定义其类型,在SimpleEventBus中管理一个map,各种handler。根据Event的type找到handler,如上述事件的hanlder类型为  RevealRootPopupContentHandler。

    最终        handler.onRevealRootPopupContent(this); 执行 RootPresenter 继承了这几个handler,在其也能看到注册

  @Override
    protected void onBind() {
        super.onBind();
        addRegisteredHandler(ResetPresentersEvent.getType(), this);
        addRegisteredHandler(RevealRootContentEvent.getType(), this);
        addRegisteredHandler(RevealRootLayoutContentEvent.getType(), this);
        addRegisteredHandler(RevealRootPopupContentEvent.getType(), this);
        addRegisteredHandler(LockInteractionEvent.getType(), this);
    }

    而对应handler的实现为如下

  @Override
    public void onRevealRootContent(
            final RevealRootContentEvent revealContentEvent) {
        getView().setUsingRootLayoutPanel(false);
        setInSlot(rootSlot, revealContentEvent.getContent());
    }

    public void onRevealRootLayoutContent(
            final RevealRootLayoutContentEvent revealContentEvent) {
        getView().setUsingRootLayoutPanel(true);
        setInSlot(rootSlot, revealContentEvent.getContent());
    }

    @Override
    public void onRevealRootPopupContent(
            final RevealRootPopupContentEvent revealContentEvent) {
        if (revealContentEvent.isCentered()) {
            addToPopupSlot(revealContentEvent.getContent());
        } else {
            addToPopupSlot(revealContentEvent.getContent(), false);
        }
    }

而在addToPopupSlot 方法中的实现就会调用PresenterWidget,如通过 child.getView().show();internalReveal;onReveal等。

    5)         // Initialize popup contents from the model
        getView().edit(model);

  通过P找到V,然后 执行edit(Model) ,根据不同的view,执行不同的逻辑,但大体上都是 弹出框上的 某一个 控件的事件的 定义,或者说 Model的   setItems ,setXXX等用户的行为触发的 事件 等。

转载于:https://my.oschina.net/ovirtKg/blog/730279

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值