midpoint源码阅读三(界面—Wicket table解析)

这篇文章将简单介绍下midpoint中的界面采用的技术和介绍一下用这些技术midpoint是如何做出一个table的。

1、GUI技术简介
midpoint前端使用的框架是wicket、福客户端技术采用Bootstrap框架和 AdminLTE template。
官网对其GUI的介绍可看这个网址:https://wiki.evolveum.com/display/midPoint/GUI+Development+Guide
wicket的界面编程参看wiki中的介绍更易上手,该地址为:https://cwiki.apache.org/confluence/display/WICKET/Reference+library

2、table 数据获取源码分析
本人从用户列表页进行wicket table编程的源码剖析。该页面如下
源码解析

该页面的路径是:com.evolveum.midpoint.web.page.admin.users.PageUsers
该页面对于的html页面是:com/evolveum/midpoint/web/page/admin/users/PageUsers.html

在正式开始介绍midpoint列表页面前,现说一些wicket编程的规范
wicket是Java平台下一个面向组件的web应用程序开源框架。每个组件均由一个 Java 类和一个 HTML 文件组成。默认情况下需要Java类和HTML放在同一个目录下(当然也可以放在不同的目录,但是需要进行一番设置,在此不详述)。
wicket table的编程示例可以在该页面学习:https://cwiki.apache.org/confluence/display/WICKET/Simple+Sortable+DataTable+Example
从该示例教程中我们知道 wicket table编程,关键是需要有一个provider类,自己实现iterator方法为table提供数据来源

源码解析

PageUsers.html页面源码如下

<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ 该页面仅描述了table的代码,并为包含上图中的 页头、菜单,本篇主要介绍table,页头、菜单的源码解析后续篇章在记录
  -->

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<body>
<wicket:extend>
    <form wicket:id="mainForm" class="form-inline">
        <div wicket:id="table"/>
    </form>
</wicket:extend>
</body>
</html>

在该页面中table 创建的核心代码是

public class PageUsers extends PageAdminUsers {

    private static final String ID_MAIN_FORM = "mainForm";
    private static final String ID_TABLE = "table";

    public PageUsers(boolean clearPagingInSession, final UsersDto.SearchType type, final String text) {
        initLayout();
    }

    private void initLayout() {
        Form mainForm = new com.evolveum.midpoint.web.component.form.Form(ID_MAIN_FORM);
        add(mainForm);

        initTable(mainForm);
    }

    private void initTable(Form mainForm) {
        Collection<SelectorOptions<GetOperationOptions>> options = new ArrayList<>();
        MainObjectListPanel<UserType> userListPanel = new MainObjectListPanel<UserType>(ID_TABLE,
                UserType.class, TableId.TABLE_USERS, options, this) {
            private static final long serialVersionUID = 1L;

            @Override
            protected List<IColumn<SelectableBean<UserType>, String>> createColumns() {
                return PageUsers.this.initColumns();
            }

            @Override
            protected PrismObject<UserType> getNewObjectListObject(){
                return (new UserType()).asPrismObject();
            }

            @Override
            protected IColumn<SelectableBean<UserType>, String> createActionsColumn() {
                return new InlineMenuButtonColumn<SelectableBean<UserType>>(createRowActions(false), 3, PageUsers.this){
                    @Override
                    protected int getHeaderNumberOfButtons() {
                        return 2;
                    }

                    @Override
                    protected List<InlineMenuItem> getHeaderMenuItems() {
                        return createRowActions(true);
                    }
                };
            }

            @Override
            protected List<InlineMenuItem> createInlineMenu() {
                return createRowActions(false);
            }

            @Override
            protected void objectDetailsPerformed(AjaxRequestTarget target, UserType object) {
                userDetailsPerformed(target, object.getOid());
            }

            @Override
            protected void newObjectPerformed(AjaxRequestTarget target) {
                navigateToNext(PageUser.class);
            }
        };

        userListPanel.setAdditionalBoxCssClasses(GuiStyleConstants.CLASS_OBJECT_USER_BOX_CSS_CLASSES);
        userListPanel.setOutputMarkupId(true);
        mainForm.add(userListPanel);
    }

    private List<IColumn<SelectableBean<UserType>, String>> initColumns() {
        List<IColumn<SelectableBean<UserType>, String>> columns = new ArrayList<>();

        IColumn<SelectableBean<UserType>, String> column = new PropertyColumn(
                createStringResource("UserType.givenName"), UserType.F_GIVEN_NAME.getLocalPart(),
                SelectableBean.F_VALUE + ".givenName");
        columns.add(column);

        column = new PropertyColumn(createStringResource("UserType.familyName"),
                UserType.F_FAMILY_NAME.getLocalPart(), SelectableBean.F_VALUE + ".familyName");
        columns.add(column);

        column = new PropertyColumn(createStringResource("UserType.fullName"),
                UserType.F_FULL_NAME.getLocalPart(), SelectableBean.F_VALUE + ".fullName");
        columns.add(column);

        column = new PropertyColumn(createStringResource("UserType.emailAddress"), null,
                SelectableBean.F_VALUE + ".emailAddress");
        columns.add(column);

        column = new AbstractExportableColumn<SelectableBean<UserType>, String>(
                createStringResource("pageUsers.accounts")) {

            @Override
            public void populateItem(Item<ICellPopulator<SelectableBean<UserType>>> cellItem,
                    String componentId, IModel<SelectableBean<UserType>> model) {
                cellItem.add(new Label(componentId,
                        model.getObject().getValue() != null ?
                                model.getObject().getValue().getLinkRef().size() : null));
            }

            @Override
            public IModel<String> getDataModel(IModel<SelectableBean<UserType>> rowModel) {
                return Model.of(rowModel.getObject().getValue() != null ?
                        Integer.toString(rowModel.getObject().getValue().getLinkRef().size()) : "");
            }


        };

        columns.add(column);

        return columns;
    }


    private void userDetailsPerformed(AjaxRequestTarget target, String oid) {
        PageParameters parameters = new PageParameters();
        parameters.add(OnePageParameterEncoder.PARAMETER, oid);
        navigateToNext(PageUser.class, parameters);
    }

}

上述代码中table创建的语句是

//该语句首先定义了一个MainObjectListPanel的匿名继承类,然后创建这个匿名类
MainObjectListPanel<UserType> userListPanel = new MainObjectListPanel<UserType>(ID_TABLE,
                UserType.class, TableId.TABLE_USERS, options, this){

                }

MainObjectListPanel是 midpoint封装的一个类,本次分析table 数据获取有关的核心代码为

public abstract class MainObjectListPanel<O extends ObjectType> extends ObjectListPanel<O> {

    public MainObjectListPanel(String id, Class<O> type, TableId tableId, Collection<SelectorOptions<GetOperationOptions>> options, PageBase parentPage) {
        super(id, type, tableId, options, parentPage);
    }
}

该类继承自ObjectListPanel,我们在看看这个类

//是table数据来源的主要实现类,从该类的createTable方法中,我们知道了,
// table数据的提供类是SelectableBeanObjectDataProvider

public abstract class ObjectListPanel<O extends ObjectType> extends BasePanel<O> {
    private static final String ID_MAIN_FORM = "mainForm";
    private static final String ID_TABLE = "table";

    /**
     * @param defaultType specifies type of the object that will be selected by default. It can be changed.
     */
    public ObjectListPanel(String id, Class<? extends O> defaultType, TableId tableId, Collection<SelectorOptions<GetOperationOptions>> options,
            PageBase parentPage) {
        this(id, defaultType, tableId, options, false, parentPage, null);
    }

    /**
     * @param defaultType specifies type of the object that will be selected by default. It can be changed.
     */
    ObjectListPanel(String id, Class<? extends O> defaultType, TableId tableId, boolean multiselect, PageBase parentPage) {
        this(id, defaultType, tableId, null, multiselect, parentPage, null);
    }

    public ObjectListPanel(String id, Class<? extends O> defaultType, TableId tableId, Collection<SelectorOptions<GetOperationOptions>> options,
                           boolean multiselect, PageBase parentPage, List<O> selectedObjectsList) {
        super(id);
        this.type = defaultType  != null ? ObjectTypes.getObjectType(defaultType) : null;
        this.parentPage = parentPage;
        this.options = options;
        this.multiselect = multiselect;
        this.selectedObjects = selectedObjectsList;
        this.tableId = tableId;
        initLayout();
    }


    private void initLayout() {
        Form<O> mainForm = new com.evolveum.midpoint.web.component.form.Form<>(ID_MAIN_FORM);
        add(mainForm);

        ........

        BoxedTablePanel<SelectableBean<O>> table = createTable();
        mainForm.add(table);

    }

    private BoxedTablePanel<SelectableBean<O>> createTable() {

        List<IColumn<SelectableBean<O>, String>> columns;
        if (isCustomColumnsListConfigured()){
            columns = initCustomColumns();
        } else {
            columns = initColumns();
        }

        BaseSortableDataProvider<SelectableBean<O>> provider = initProvider();


        BoxedTablePanel<SelectableBean<O>> table = new BoxedTablePanel<SelectableBean<O>>(ID_TABLE, provider,
                columns, tableId, tableId == null ? 10 : parentPage.getSessionStorage().getUserProfile().getPagingSize(tableId)) {
            private static final long serialVersionUID = 1L;

            @Override
            protected WebMarkupContainer createHeader(String headerId) {
                return ObjectListPanel.this.createHeader(headerId);
            }

            @Override
            public String getAdditionalBoxCssClasses() {
                return ObjectListPanel.this.getAdditionalBoxCssClasses();
            }

            @Override
            protected WebMarkupContainer createButtonToolbar(String id) {
                WebMarkupContainer bar = ObjectListPanel.this.createTableButtonToolbar(id);

                return bar != null ? bar : super.createButtonToolbar(id);
            }

        };
        table.setOutputMarkupId(true);
        String storageKey = getStorageKey();
        if (StringUtils.isNotEmpty(storageKey)) {
            PageStorage storage = getPageStorage(storageKey);
            if (storage != null) {
                table.setCurrentPage(storage.getPaging());
            }
        }

        return table;
    }

    protected List<IColumn<SelectableBean<O>, String>> initCustomColumns() {
        LOGGER.trace("Start to init custom columns for table of type {}", type);
        List<IColumn<SelectableBean<O>, String>> columns = new ArrayList<>();
        List<GuiObjectColumnType> customColumns = getGuiObjectColumnTypeList();
        if (customColumns == null){
            return columns;
        }

        CheckBoxHeaderColumn<SelectableBean<O>> checkboxColumn = (CheckBoxHeaderColumn<SelectableBean<O>>) createCheckboxColumn();
        if (checkboxColumn != null) {
            columns.add(checkboxColumn);
        }

        IColumn<SelectableBean<O>, String> iconColumn = (IColumn) ColumnUtils.createIconColumn(type.getClassDefinition());
        columns.add(iconColumn);

        columns.addAll(getCustomColumnsTransformed(customColumns));
        IColumn<SelectableBean<O>, String> actionsColumn = createActionsColumn();
        if (actionsColumn != null){
            columns.add(actionsColumn);
        }
        LOGGER.trace("Finished to init custom columns, created columns {}", columns);
        return columns;
    }

    protected List<IColumn<SelectableBean<O>, String>> initColumns() {
        LOGGER.trace("Start to init columns for table of type {}", type);
        List<IColumn<SelectableBean<O>, String>> columns = new ArrayList<>();

        CheckBoxHeaderColumn<SelectableBean<O>> checkboxColumn = (CheckBoxHeaderColumn<SelectableBean<O>>) createCheckboxColumn();
        if (checkboxColumn != null) {
            columns.add(checkboxColumn);
        }

        IColumn<SelectableBean<O>, String> iconColumn = (IColumn) ColumnUtils.createIconColumn(type.getClassDefinition());
        columns.add(iconColumn);

        IColumn<SelectableBean<O>, String> nameColumn = createNameColumn(null, null);
        columns.add(nameColumn);

        List<IColumn<SelectableBean<O>, String>> others = createColumns();
        columns.addAll(others);
        IColumn<SelectableBean<O>, String> actionsColumn = createActionsColumn();
        if (actionsColumn != null) {
            columns.add(createActionsColumn());
        }
        LOGGER.trace("Finished to init columns, created columns {}", columns);
        return columns;
    }

    protected BaseSortableDataProvider<SelectableBean<O>> initProvider() {
        Set<O> selectedObjectsSet = selectedObjects == null ? null : new HashSet<>(selectedObjects);
        SelectableBeanObjectDataProvider<O> provider = new SelectableBeanObjectDataProvider<O>(
                parentPage, (Class) type.getClassDefinition(), selectedObjectsSet) {
            private static final long serialVersionUID = 1L;

            @Override
            protected void saveProviderPaging(ObjectQuery query, ObjectPaging paging) {
                String storageKey = getStorageKey();
                if (StringUtils.isNotEmpty(storageKey)) {
                    PageStorage storage = getPageStorage(storageKey);
                    if (storage != null) {
                        storage.setPaging(paging);
                    }
                }
            }

            @Override
            public SelectableBean<O> createDataObjectWrapper(O obj) {
                SelectableBean<O> bean = super.createDataObjectWrapper(obj);
                List<InlineMenuItem> inlineMenu = createInlineMenu();
                if (inlineMenu != null) {
                    bean.getMenuItems().addAll(inlineMenu);
                }
                return bean;
            }

            @Override
            protected List<ObjectOrdering> createObjectOrderings(SortParam<String> sortParam) {
                List<ObjectOrdering> customOrdering =  createCustomOrdering(sortParam);
                if (customOrdering != null) {
                    return customOrdering;
                }
                return super.createObjectOrderings(sortParam);
            }
        };
        if (options == null){
            if (ResourceType.class.equals(type)) {
                options = SelectorOptions.createCollection(GetOperationOptions.createNoFetch());
            }
        } else {
            if (ResourceType.class.equals(type)) {
                GetOperationOptions root = SelectorOptions.findRootOptions(options);
                root.setNoFetch(Boolean.TRUE);
            }
            provider.setOptions(options);
        }
        provider.setQuery(getQuery());

        return provider;
    }

    protected abstract IColumn<SelectableBean<O>, String> createCheckboxColumn();

    protected abstract IColumn<SelectableBean<O>, String> createNameColumn(IModel<String> columnNameModel, String itemPath);

    protected abstract List<IColumn<SelectableBean<O>, String>> createColumns();

    protected IColumn<SelectableBean<O>, String> createActionsColumn(){
        return null;
    }

    protected abstract List<InlineMenuItem> createInlineMenu();

    ........
}

我们看一下SelectableBeanObjectDataProvider类

//从该类中我们看到该Provider类中,并未有wicket默认Provider应该实现的iterator方法,
//继续查看父类,我们可在BaseSortableDataProvider类中找到该方法,在此就不再列代码了
//仅做一个说明,BaseSortableDataProvider类的iterator方法,调用的是internalIterator方法
//我们详细看看这个方法发现,获取数据的核心语句是
//List<PrismObject<? extends O>> list = (List)getModel().searchObjects(type, query, currentOptions, task, result);
//该语句中的getModel()返回的是怎样一个类呢,单步调试,我们发现该类是
//com.evolveum.midpoint.model.impl.controller.ModelController

public class SelectableBeanObjectDataProvider<O extends ObjectType> extends BaseSortableDataProvider<SelectableBean<O>> {
    public SelectableBeanObjectDataProvider(Component component, Class<? extends O> type, Set<? extends O> selected ) {
        super(component, true, true);
    }

    @Override
    public Iterator<SelectableBean<O>> internalIterator(long offset, long pageSize) {
        LOGGER.trace("begin::iterator() offset {} pageSize {}.", new Object[]{offset, pageSize});

        preprocessSelectedData();

        OperationResult result = new OperationResult(OPERATION_SEARCH_OBJECTS);
        try {
            ObjectPaging paging = createPaging(offset, pageSize);
            Task task = getPage().createSimpleTask(OPERATION_SEARCH_OBJECTS);

            ObjectQuery query = getQuery();
            if (query == null){
                if (emptyListOnNullQuery) {
                    return new ArrayList<SelectableBean<O>>().iterator();
                }
                query = new ObjectQuery();
            }
            query.setPaging(paging);

            if (LOGGER.isTraceEnabled()) {
                LOGGER.trace("Query {} with {}", type.getSimpleName(), query.debugDump());
            }

            if (ResourceType.class.equals(type) && (options == null || options.isEmpty())){
                options = SelectorOptions.createCollection(GetOperationOptions.createNoFetch());
            }
            Collection<SelectorOptions<GetOperationOptions>> currentOptions = options;
            if (export) {
                // TODO also for other classes
                if (ShadowType.class.equals(type)) {
                    currentOptions = SelectorOptions.set(currentOptions, ItemPath.EMPTY_PATH, () -> new GetOperationOptions(),
                            (o) -> o.setDefinitionProcessing(ONLY_IF_EXISTS));
                    currentOptions = SelectorOptions
                            .set(currentOptions, new ItemPath(ShadowType.F_FETCH_RESULT), GetOperationOptions::new,
                                    (o) -> o.setDefinitionProcessing(FULL));
                    currentOptions = SelectorOptions
                            .set(currentOptions, new ItemPath(ShadowType.F_AUXILIARY_OBJECT_CLASS), GetOperationOptions::new,
                                    (o) -> o.setDefinitionProcessing(FULL));
                }
            }
            List<PrismObject<? extends O>> list = (List)getModel().searchObjects(type, query, currentOptions, task, result);

            if (LOGGER.isTraceEnabled()) {
                LOGGER.trace("Query {} resulted in {} objects", type.getSimpleName(), list.size());
            }

            for (PrismObject<? extends O> object : list) {
                getAvailableData().add(createDataObjectWrapper(object.asObjectable()));
            }
//            result.recordSuccess();
        } catch (Exception ex) {
            result.recordFatalError("Couldn't list objects.", ex);
            LoggingUtils.logUnexpectedException(LOGGER, "Couldn't list objects", ex);
            return handleNotSuccessOrHandledErrorInIterator(result);
        } finally {
            result.computeStatusIfUnknown();
        }

        LOGGER.trace("end::iterator() {}", result);
        return getAvailableData().iterator();
    }

    @Override
    protected int internalSize() {
        LOGGER.trace("begin::internalSize()");
        if (!isUseObjectCounting()) {
            return Integer.MAX_VALUE;
        }
        int count = 0;
        Task task = getPage().createSimpleTask(OPERATION_COUNT_OBJECTS);
        OperationResult result = task.getResult();
        try {
            Integer counted = getModel().countObjects(type, getQuery(), options, task, result);
            count = defaultIfNull(counted, 0);
        } catch (Exception ex) {
            result.recordFatalError("Couldn't count objects.", ex);
            LoggingUtils.logUnexpectedException(LOGGER, "Couldn't count objects", ex);
        } finally {
            result.computeStatusIfUnknown();
        }

        if (!WebComponentUtil.isSuccessOrHandledError(result) && !result.isNotApplicable()) {
            getPage().showResult(result);
            // Let us do nothing. The error will be shown on the page and a count of 0 will be used.
            // Redirecting to the error page does more harm than good (see also MID-4306).
        }

        LOGGER.trace("end::internalSize(): {}", count);
        return count;
    }

}

我们来看一下com.evolveum.midpoint.model.impl.controller.ModelController中的searchObject方法。

//通过看该方法,我们知道获取数据的语句是
//case REPOSITORY: list = cacheRepositoryService.searchObjects(type, query, options, result); break;

public class ModelController implements ModelService, TaskService, WorkflowService, ScriptingService, AccessCertificationService, CaseManagementService {
    @Override
    public <T extends ObjectType> SearchResultList<PrismObject<T>> searchObjects(Class<T> type, ObjectQuery query,
            Collection<SelectorOptions<GetOperationOptions>> rawOptions, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {

        Validate.notNull(type, "Object type must not be null.");
        Validate.notNull(parentResult, "Operation result must not be null.");
        if (query != null) {
            ModelImplUtils.validatePaging(query.getPaging());
        }

        OperationResult result = parentResult.createSubresult(SEARCH_OBJECTS);
        result.addParam(OperationResult.PARAM_TYPE, type);
        result.addParam(OperationResult.PARAM_QUERY, query);

        Collection<SelectorOptions<GetOperationOptions>> options = preProcessOptionsSecurity(rawOptions, task, result);
        GetOperationOptions rootOptions = SelectorOptions.findRootOptions(options);

        ObjectTypes.ObjectManager searchProvider = ObjectTypes.getObjectManagerForClass(type);
        if (searchProvider == null || searchProvider == ObjectTypes.ObjectManager.MODEL || GetOperationOptions.isRaw(rootOptions)) {
            searchProvider = ObjectTypes.ObjectManager.REPOSITORY;
        }
        result.addArbitraryObjectAsParam("searchProvider", searchProvider);

        query = preProcessQuerySecurity(type, query, task, result);
        if (isFilterNone(query, result)) {
            return new SearchResultList<>(new ArrayList<>());
        }

        SearchResultList<PrismObject<T>> list;
        try {
            enterModelMethod();
            logQuery(query);

            try {
                if (GetOperationOptions.isRaw(rootOptions)) {       // MID-2218
                    QNameUtil.setTemporarilyTolerateUndeclaredPrefixes(true);
                }
                switch (searchProvider) {
                    case EMULATED: list = emulatedSearchProvider.searchObjects(type, query, options, result); break;
                    case REPOSITORY: list = cacheRepositoryService.searchObjects(type, query, options, result); break;
                    case PROVISIONING: list = provisioning.searchObjects(type, query, options, task, result); break;
                    case TASK_MANAGER:
                        list = taskManager.searchObjects(type, query, options, result);
                        if (workflowManager != null && TaskType.class.isAssignableFrom(type) && !GetOperationOptions.isRaw(rootOptions) && !GetOperationOptions.isNoFetch(rootOptions)) {
                            workflowManager.augmentTaskObjectList(list, options, task, result);
                        }
                        break;
                    default: throw new AssertionError("Unexpected search provider: " + searchProvider);
                }
                result.computeStatus();
                result.cleanupResult();
            } catch (CommunicationException | ConfigurationException | SchemaException | SecurityViolationException | RuntimeException | ObjectNotFoundException e) {
                processSearchException(e, rootOptions, searchProvider, result);
                throw e;
            } finally {
                QNameUtil.setTemporarilyTolerateUndeclaredPrefixes(false);
                if (LOGGER.isTraceEnabled()) {
                    LOGGER.trace(result.dump(false));
                }
            }

            if (list == null) {
                list = new SearchResultList<>(new ArrayList<PrismObject<T>>());
            }

            for (PrismObject<T> object : list) {
                if (hookRegistry != null) {
                    for (ReadHook hook : hookRegistry.getAllReadHooks()) {
                        hook.invoke(object, options, task, result);
                    }
                }
                executeResolveOptions(object.asObjectable(), options, task, result);
            }

            // postprocessing objects that weren't handled by their correct provider (e.g. searching for ObjectType, and retrieving tasks, resources, shadows)
            // currently only resources and shadows are handled in this way
            // TODO generalize this approach somehow (something like "postprocess" in task/provisioning interface)
            if (searchProvider == ObjectTypes.ObjectManager.REPOSITORY && !GetOperationOptions.isRaw(rootOptions)) {
                for (PrismObject<T> object : list) {
                    if (object.asObjectable() instanceof ResourceType || object.asObjectable() instanceof ShadowType) {
                        provisioning.applyDefinition(object, task, result);
                    }
                }
            }
            // better to use cache here (MID-4059)
            schemaTransformer.applySchemasAndSecurityToObjects(list, rootOptions, options, null, task, result);

        } finally {
            exitModelMethod();
        }

        return list;
    }

    @Override
    public <T extends Containerable> Integer countContainers(
            Class<T> type, ObjectQuery query, Collection<SelectorOptions<GetOperationOptions>> rawOptions,
            Task task, OperationResult parentResult) throws SchemaException, SecurityViolationException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException {

        Validate.notNull(type, "Container value type must not be null.");
        Validate.notNull(parentResult, "Result type must not be null.");

        final OperationResult result = parentResult.createSubresult(SEARCH_CONTAINERS);
        result.addParam(OperationResult.PARAM_TYPE, type);
        result.addParam(OperationResult.PARAM_QUERY, query);

        final ContainerOperationContext<T> ctx = new ContainerOperationContext<>(type, query, task, result);

        final Collection<SelectorOptions<GetOperationOptions>> options = preProcessOptionsSecurity(rawOptions, task, result);
        final GetOperationOptions rootOptions = SelectorOptions.findRootOptions(options);

        query = ctx.refinedQuery;

        if (isFilterNone(query, result)) {
            return 0;
        }

        Integer count;
        try {
            enterModelMethod();

            logQuery(query);

            try {
                switch (ctx.manager) {
                    case REPOSITORY: count = cacheRepositoryService.countContainers(type, query, options, result); break;
                    case WORKFLOW: count = workflowManager.countContainers(type, query, options, result); break;
                    default: throw new IllegalStateException();
                }
                result.computeStatus();
                result.cleanupResult();
            } catch (SchemaException|RuntimeException e) {
                processSearchException(e, rootOptions, ctx.manager, result);
                throw e;
            } finally {
                if (LOGGER.isTraceEnabled()) {
                    LOGGER.trace(result.dump(false));
                }
            }
        } finally {
            exitModelMethod();
        }

        return count;
    }

}

cacheRepositoryService通过查看源码可知,其对于的类为com.evolveum.midpoint.repo.cache.RepositoryCache

@Component(value="cacheRepositoryService")
public class RepositoryCache implements RepositoryService {}

该分析就分析到这块儿,其后就是midpoint底层存储库的核心代码了,在此不深入描述,本篇主要介绍如何使用midpoint提供的存储库接口 实现table的展示。另外,上面代码列创建的核心代码已列出,table中的列如何创建,请自行看代码。

下对刚才的代码做个简短的总结,midpoint源码创建table的方式是

1、BoxedTablePanel<SelectableBean<O>> table = new BoxedTablePanel<SelectableBean<O>>(ID_TABLE, provider,
                columns, tableId, pagesize)
2、实现provider的iterator方法
3、cacheRepositoryService.searchObjects(type, query, options, result); 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风水月

从心底相信自己是成功的第一步

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值