Показаны сообщения с ярлыком ADF. Показать все сообщения
Показаны сообщения с ярлыком ADF. Показать все сообщения

15.09.2016

Модификация запроса View Criteria в runtime

Для того, чтобы модифицировать предикаты, которыми оборачивается запрос из View Criteria, применяется перекрытый метод getCriteriaItemClause класса ViewObjectImpl. Например, мы можем вручную подцепить определеную bind-переменную в произвольное ограничение:
@Override
public String getCriteriaItemClause(ViewCriteriaItem aVCI) {
            VariableValueManager vm = this.ensureVariableManager();
            ArrayList criteriaList = aVCI.getValues();
            for (ViewCriteriaItemValue itemValue : criteriaList) {
                if (itemValue.getIsBindVar()) {
                    Variable bindVariable = itemValue.getBindVariable();
                    Object variableValue = vm.getVariableValue(bindVariable);
                    // check for the special DESCRIPTION in the used bind variable
                    Object descProperty = bindVariable.getProperty("DESCRIPTION");
                    String description = (descProperty != null ? descProperty.toString() : "null");
                    if ("NAME".equalsIgnoreCase(description)) {
                        if (aVCI.getViewCriteria().getRootViewCriteria().isCriteriaForQuery()) {
                            if (variableValue != null) {
                                return "EMP.NAME LIKE ('%' || :p_name || '%')";
                            }
                        }
                    }
                }
            }
            return super.getCriteriaItemClause(aVCI);
        }
Про происходит: выполняется цикл по всем bind-переменным. Идентификация нужной переменной происходит по свойству DESCRIPTION. Если переменная найдена и она имеет значение, отличное от NULL, добавляется предикат. Иначе вызывается родительский метод.

Ещё нюанс о галочке Ignore Null Values с обязательными bind-переменными:
Снятая галочка Ignore Null Values в случае с обязательными bind-переменными обеспечивает оборачивание запроса в ограничение ( = :p_var or :p_var is null) в любом случае, что в случае со сложными запросами может привести к сбиванию плана. Если галочка установлена, то ограничение добавляется только если переменная не NULL.

04.05.2016

Jdeveloper Design Time issue fix

Дополню пост про устранение глюка с отображением страниц в JDeveloper.
Для версии 11.1.2.2.39.61.83.1 нужно удалить следующие каталоги:

  • o.jsp.feature
  • o.j2ee.jstl
  • o.j2ee.jpsconfig
  • o.ide.appoverview
  • o.j2ee.jsplib

12.02.2016

ADF: кастомный selection-listener и обновление данных в popup

Столкнулся с проблемой: не обновляется отображение переменной биндинга в popup-окне. Решается оборачиванием стандартного листенера в кастомный метод и программным добавлением partial trigger на элемент, инициирующий обновление переменной.
    public void customListener(SelectionEvent selectionEvent) {
        UIComponent component = selectionEvent.getComponent();
        component.processUpdates(FacesContext.getCurrentInstance());
        JSFUtils.resolveMethodExpression("#{bindings.AllUsersVO1.collectionModel.makeCurrent}", null,
                                          new Class[]{SelectionEvent.class}, new Object[]{selectionEvent});
        setExpressionValueCustom();
    }
   
    private void setExpressionValueCustom(){
        ViewRowImpl row = (ViewRowImpl)JSFUtils.resolveExpression("#{bindings.AllUsersVO1.currentRow}");
        JSFUtils.setExpressionValue("#{bindings.userLogin.inputValue}", row.getAttribute("Username"));
        AdfFacesContext.getCurrentInstance().addPartialTarget(getP_delete());
    }
getP_delete() - геттер объекта RichPopup.

21.01.2016

ADF: Уход со страницы с незафиксированными данными

У концепции ADF есть интересное свойство: обязанность по отслеживанию незакоммиченных данных полностью ложится на программиста. Сложно сказать, как с этим планировалось справляться силами недоучившихся студентов, на которых рассчитано декларативное программирование в ADF. Да и не об этом речь. Речь пойдет о том, как уйти со страницы, которая создала новую запись в VO, связанном c EO, не зафиксировать эту запись, и потом благополучно вернуться обратно.

Что сразу приходит в голову:
    @PostConstruct
    public void initBean() {
        AppModuleImpl mod = (AppModuleImpl)ADFUtils.getBindingApplicationModule();
        DBTransaction conn = mod.getDBTransaction();
        if (conn.isDirty()) conn.rollback();
        ...
Так нехорошо, потому что initBean() будет вызываться при каждой активности на странице и транзакция будет теряться тогда, когда это не нужно. К тому же  транзакция будет откатываться на фазе prepareRender, что тоже идеологически неверно. То есть контекст уже проинициализирован, загружена модель данных, проверены валидации, всё готово к прорисовке страницы - и тут мы сообщаем системе, что данные нужно изменить. Не знаю, что именно будет происходить глубоко внутри приложения, но думаю, что ничего хорошего.

Рабочее решение заключается в создании бина уровня приложения, унаследованного от PagePhaseListener, где мы будем анализировать страницу на ранней фазе жизненного цикла и отслеживать грязные данные. Создаем бин и прописываем его в adf-settings.xml:


Сам класс:
    import java.util.ArrayList;
    import java.util.Map;
    import javax.faces.context.FacesContext;

    import javax.servlet.http.HttpServletRequest;
   
    import oracle.adf.controller.v2.lifecycle.PagePhaseEvent;
    import oracle.adf.controller.v2.lifecycle.PagePhaseListener;

    import oracle.adf.share.ADFContext;
   
    import oracle.jbo.server.DBTransaction;
   
    import *.ADFUtils;
    import *.AppModuleImpl;

public class PageListenerBean implements PagePhaseListener{
   
    // если conn.isDirty() и переходим на из url из списка, то rollback
    private static String[] deniedUrls = {"/myapp/faces/task-flow-definition/tree"};
   
    @Override
    public void beforePhase(PagePhaseEvent pagePhaseEvent) {
            try{
                    if (pagePhaseEvent.getPhaseId() == 9){
                        AppModuleImpl mod = (AppModuleImpl)ADFUtils.getBindingApplicationModule();
                        DBTransaction conn = mod.getDBTransaction();
                        if (isPageChanged()){
                            if (changedToDisallowed()){
                                if (conn.isDirty()){
                                    conn.rollback();
                                    System.out.println("= rolled back!");
                                }
                            }
                        }
                     }
            }catch(Exception e){
                ;          
            }
    }
   
    @Override
    public void afterPhase(PagePhaseEvent pagePhaseEvent) {
        ;       
    }
   
    private void setApplicationScopeParameter(String val){
        ADFContext adfCtx =  ADFContext.getCurrent();
        Map applicationScope = adfCtx.getApplicationScope();
        applicationScope.put("currentURL", val);
    }
   
    private String getApplicationScopeParameter(){
        ADFContext adfCtx =  ADFContext.getCurrent();
        Map applicationScope   = adfCtx.getApplicationScope();
        return (String)applicationScope.get("currentURL");
    }
   
    private boolean isPageChanged(){
        try{
            if (!getCurrentUrl().equals(getApplicationScopeParameter())){
                setApplicationScopeParameter(getCurrentUrl());
                return true;
            }
        }catch(NullPointerException e){
            System.out.println("--! PageListenerBean: can't get current url");
        }
        return false;
    }
   
    private boolean changedToDisallowed(){
        for(String url : this.deniedUrls)
            if (url.equals(getCurrentUrl()))
                return true;
        return false;     
    }
   
    private String getCurrentUrl(){
        FacesContext ctx = FacesContext.getCurrentInstance();
        HttpServletRequest servletRequest = (HttpServletRequest) ctx.getExternalContext().getRequest();
        return servletRequest.getRequestURI();
    }
}
При каждом срабатывании проверяется соответствие текущего URL переменной #{applicationScope.currentURL}. Если страница отличается, находится в "стоп-листе" и данные грязные, тогда откатываем транзакцию. Стоп-лист введен во избежание непредвиденных потерь транзакции при единой фиксации нескольких страниц (такое может быть в приложении).

Код 9 - id фазы инициализаци Restore View. Интересно проследить жизненный цикл страницы-источника (откуда переходим) после изменения данных:

В целевой странице (куда переходим)  грязные данные присутствуют во всем цикле: